Static interface to read Nit objects from JSON strings

Text::parse_json returns a simple Nit object from the JSON source. This object can then be type checked as usual with isa and as.

Introduced classes

class JSONStringParser

json :: JSONStringParser

A simple ad-hoc JSON parser
class JsonArray

json :: JsonArray

A JSON array.
interface JsonMapRead[K: String, V: nullable Serializable]

json :: JsonMapRead

A map that can be translated into a JSON object.
class JsonObject

json :: JsonObject

A JSON Object.
class JsonSequenceRead[E: nullable Serializable]

json :: JsonSequenceRead

A sequence that can be translated into a JSON array.

Redefined classes

redef enum Char

json :: static $ Char

Native characters.
redef abstract class FlatText

json :: static $ FlatText

All kinds of array-based text representations.
redef abstract class Text

json :: static $ Text

High-level abstraction for all text representations

All class definitions

redef enum Char

json :: static $ Char

Native characters.
redef abstract class FlatText

json :: static $ FlatText

All kinds of array-based text representations.
class JSONStringParser

json $ JSONStringParser

A simple ad-hoc JSON parser
class JsonArray

json $ JsonArray

A JSON array.
interface JsonMapRead[K: String, V: nullable Serializable]

json $ JsonMapRead

A map that can be translated into a JSON object.
class JsonObject

json $ JsonObject

A JSON Object.
class JsonSequenceRead[E: nullable Serializable]

json $ JsonSequenceRead

A sequence that can be translated into a JSON array.
redef abstract class Text

json :: static $ Text

High-level abstraction for all text representations
package_diagram json::static static json::error error json::static->json::error parser_base parser_base json::error->parser_base ...parser_base ... ...parser_base->parser_base json::serialization_read serialization_read json::serialization_read->json::static json::dynamic dynamic json::dynamic->json::static popcorn::pop_validation pop_validation popcorn::pop_validation->json::static nitc::json_commands json_commands nitc::json_commands->json::static json::json json json::json->json::serialization_read json::json... ... json::json...->json::json a_star-m a_star-m a_star-m->json::dynamic a_star-m... ... a_star-m...->a_star-m popcorn::pop_json pop_json popcorn::pop_json->popcorn::pop_validation popcorn::pop_json... ... popcorn::pop_json...->popcorn::pop_json nitc::api_base api_base nitc::api_base->nitc::json_commands nitc::api_base... ... nitc::api_base...->nitc::api_base

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module array

core :: array

This module introduces the standard array structure.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module list

core :: list

This module handle double linked lists
module math

core :: math

Mathematical operations
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module native

core :: native

Native structures for text and bytes
module numeric

core :: numeric

Advanced services for Numeric types
module parser_base

parser_base :: parser_base

Simple base for hand-made parsers of all kinds
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O

Parents

module error

json :: error

Intro JsonParseError which is exposed by all JSON reading APIs

Children

module dynamic

json :: dynamic

Dynamic interface to read values from JSON strings
module json_commands

nitc :: json_commands

Translate command results to json
module pop_validation

popcorn :: pop_validation

Quick and easy validation framework for Json inputs
module serialization_read

json :: serialization_read

Services to read JSON: deserialize_json and JsonDeserializer

Descendants

module a_star-m

a_star-m

module api

github :: api

Nit object oriented interface to Github api.
module api

nitc :: api

Components required to build a web server about the nit model.
module api_auth

nitc :: api_auth

module api_base

nitc :: api_base

Base classes used by nitweb.
module api_docdown

nitc :: api_docdown

Nitdoc specific Markdown format handling for Nitweb
module api_feedback

nitc :: api_feedback

Feedback related features
module api_light

nitc :: api_light

Highlight and collect messages from a piece of code
module api_model

nitc :: api_model

module at_boot

android :: at_boot

Import this module to launch Service at device boot
module bundle

android :: bundle

A mapping class of String to various value types used by the
module cache

github :: cache

Enable caching on Github API accesses.
module client

gamnit :: client

Client-side network services for games and such
module common

gamnit :: common

Services common to the client and server modules
module commonmark_gen

markdown2 :: commonmark_gen

Generate Nitunit tests from commonmark specification.
module curl_json

neo4j :: curl_json

cURL requests compatible with the JSON REST APIs.
module curl_rest

curl :: curl_rest

module data_store

android :: data_store

Implements app::data_store using shared_preferences
module data_store

ios :: data_store

Implements app::data_store using NSUserDefaults
module data_store

linux :: data_store

app::data_store implementation on GNU/Linux
module error

neo4j :: error

Errors thrown by the neo4j library.
module events

github :: events

Events are emitted by Github Hooks.
module example_angular

popcorn :: example_angular

This is an example of how to use angular.js with popcorn
module github

github :: github

Nit wrapper for Github API
module graph

neo4j :: graph

Provides an interface for services on a Neo4j graphs.
module hooks

github :: hooks

Github hook event listening with nitcorn.
module http_request

ios :: http_request

Implementation of app::http_request for iOS
module http_request

android :: http_request

Android implementation of app:http_request
module http_request

linux :: http_request

Implementation of app::http_request using GDK and Curl
module http_request

app :: http_request

HTTP request services: AsyncHttpRequest and Text::http_get
module http_request_example

app :: http_request_example

Example for the app::http_request main service AsyncHttpRequest
module intent

android :: intent

Services allowing to launch activities and start/stop services using
module intent_api10

android :: intent_api10

Services allowing to launch activities and start/stop services using
module intent_api11

android :: intent_api11

Refines intent module to add API 11 services
module intent_api12

android :: intent_api12

Refines intent module to add API 12 services
module intent_api14

android :: intent_api14

Refines intent module to add API 14 services
module intent_api15

android :: intent_api15

Refines intent module to add API 15 services
module intent_api16

android :: intent_api16

Refines intent module to add API 16 services
module intent_api17

android :: intent_api17

Refines intent module to add API 17 services
module intent_api18

android :: intent_api18

Refines intent module to add API 18 services
module intent_api19

android :: intent_api19

Refines intent module to add API 19 services
module json

json :: json

Read and write JSON formatted text using the standard serialization services
module json_graph_store

neo4j :: json_graph_store

Provides JSON as a mean to store graphs.
module loader

github :: loader

module mongodb

mongodb :: mongodb

MongoDB Nit Driver.
module mpi

mpi :: mpi

Implementation of the Message Passing Interface protocol by wrapping OpenMPI
module msgpack

msgpack :: msgpack

MessagePack, an efficient binary serialization format
module msgpack_to_json

msgpack :: msgpack_to_json

Convert MessagePack format to JSON
module native_ui

android :: native_ui

Native services from the android.view and android.widget namespaces
module neo

nitc :: neo

Save and load a Model to/from a Neo4j graph.
module neo4j

neo4j :: neo4j

Neo4j connector through its JSON REST API using curl.
module network

gamnit :: network

Easy client/server logic for games and simple distributed applications
module nit_activity

android :: nit_activity

Core implementation of app.nit on Android using a custom Java entry point
module nitdoc

nitc :: nitdoc

Generator of static API documentation for the Nit language
module nitweb

nitc :: nitweb

Runs a webserver based on nitcorn that render things from model.
module pop_auth

popcorn :: pop_auth

Authentification handlers.
module pop_json

popcorn :: pop_json

Introduce useful services for JSON REST API handlers.
module pop_repos

popcorn :: pop_repos

Repositories for data management.
module pop_templates

popcorn :: pop_templates

Template rendering for popcorn
module queries

mongodb :: queries

Mongo queries framework
module restful

nitcorn :: restful

Support module for the nitrestful tool and the restful annotation
module restful_annot

nitcorn :: restful_annot

Example for the restful annotation documented at lib/nitcorn/restful.nit
module sequential_id

neo4j :: sequential_id

Provides a sequential identification scheme for Neo4j nodes.
module serialization_read

msgpack :: serialization_read

Deserialize full Nit objects from MessagePack format
module server

gamnit :: server

Server-side network services for games and such
module service

android :: service

Android service support for app.nit centered around the class Service
module shared_preferences

android :: shared_preferences

Services allowing to save and load datas to internal android device
module shared_preferences_api10

android :: shared_preferences_api10

Services to save/load data using android.content.SharedPreferences for the android platform
module shared_preferences_api11

android :: shared_preferences_api11

Refines shared_preferences module to add API 11 services
module static

nitc :: static

Nitdoc generation framework
module static_html

nitc :: static_html

Render documentation pages as HTML
module static_index

nitc :: static_index

Manage indexing of Nit model for Nitdoc QuickSearch.
module store

json :: store

Store and load json data.
module test_neo

nitc :: test_neo

Test for neo model saving and loading.
module ui

android :: ui

Views and services to use the Android native user interface
module ui

linux :: ui

Implementation of the app.nit UI module for GNU/Linux
module ui_test

android :: ui_test

Test for app.nit's UI services
module wallet

github :: wallet

Github OAuth tokens management
module wifi

android :: wifi

Simple wrapper of the Android WiFi services
# Static interface to read Nit objects from JSON strings
#
# `Text::parse_json` returns a simple Nit object from the JSON source.
# This object can then be type checked as usual with `isa` and `as`.
module static

import parser_base
intrude import error

redef class Text

	# Removes JSON-escaping if necessary in a JSON string
	#
	#     assert "\\\"string\\uD83D\\uDE02\\\"".unescape_json == "\"string😂\""
	fun unescape_json: Text do
		if not json_need_escape then return self
		return self.json_to_nit_string
	end

	# Does `self` need treatment from JSON to Nit ?
	#
	# i.e. is there at least one `\` character in it ?
	#
	#     assert not "string".json_need_escape
	#     assert "\\\"string\\\"".json_need_escape
	private fun json_need_escape: Bool do return has('\\')

	# Escapes `self` from a JSON string to a Nit string
	#
	#     assert "\\\"string\\\"".json_to_nit_string == "\"string\""
	#     assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
	#     assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
	private fun json_to_nit_string: String do
		var res = new FlatBuffer.with_capacity(byte_length)
		var i = 0
		var ln = self.length
		while i < ln do
			var char = self[i]
			if char == '\\' then
				i += 1
				char = self[i]
				if char == 'b' then
					char = 0x08.code_point
				else if char == 'f' then
					char = 0x0C.code_point
				else if char == 'n' then
					char = '\n'
				else if char == 'r' then
					char = '\r'
				else if char == 't' then
					char = '\t'
				else if char == 'u' then
					var u16_esc = from_utf16_digit(i + 1)
					char = u16_esc.code_point
					if char.is_surrogate and i + 10 < ln then
						if self[i + 5] == '\\' and self[i + 6] == 'u' then
							u16_esc <<= 16
							u16_esc += from_utf16_digit(i + 7)
							char = u16_esc.to_u32.from_utf16_surr.code_point
							i += 6
						else
							char = 0xFFFD.code_point
						end
					end
					i += 4
				end
				# `"`, `/` or `\` => Keep `char` as-is.
			end
			res.add char
			i += 1
		end
		return res.to_s
	end

	# Parse `self` as JSON.
	#
	# If `self` is not a valid JSON document or contains an unsupported escape
	# sequence, return a `JSONParseError`.
	#
	# Example with `JsonObject`:
	#
	#     var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".parse_json
	#     assert obj isa JsonObject
	#     assert obj["foo"] isa JsonObject
	#     assert obj["foo"].as(JsonObject)["bar"] == true
	#
	# Example with `JsonArray`:
	#
	#     var arr = "[1, 2, 3]".parse_json
	#     assert arr isa JsonArray
	#     assert arr.length == 3
	#     assert arr.first == 1
	#     assert arr.last == 3
	#
	# Example with `String`:
	#
	#     var str = "\"foo, bar, baz\"".parse_json
	#     assert str isa String
	#     assert str == "foo, bar, baz"
	#
	# Example of a syntax error:
	#
	#     var error = "\{foo: \"bar\"\}".parse_json
	#     assert error isa JsonParseError
	#     assert error.to_s == "Bad key format Error: bad JSON entity"
	fun parse_json: nullable Serializable do return (new JSONStringParser(self.to_s)).parse_entity
end

redef class FlatText
	redef fun json_need_escape do
		var its = items
		for i in [first_byte .. last_byte] do
			if its[i] == 0x5C then return true
		end
		return false
	end
end

redef class Char
	# Is `self` a valid number start ?
	private fun is_json_num_start: Bool do
		if self == '-' then return true
		if self.is_numeric then return true
		return false
	end

	# Is `self` a valid JSON separator ?
	private fun is_json_separator: Bool do
		if self == ':' then return true
		if self == ',' then return true
		if self == '{' then return true
		if self == '}' then return true
		if self == '[' then return true
		if self == ']' then return true
		if self == '"' then return true
		if self.is_whitespace then return true
		return false
	end
end

# A simple ad-hoc JSON parser
#
# To parse a simple JSON document, read it as a String and give it to `parse_entity`
# NOTE: if your document contains several non-nested entities, use `parse_entity` for each
# JSON entity to parse
class JSONStringParser
	super StringProcessor

	# Parses a JSON Entity
	#
	# ~~~nit
	# var p = new JSONStringParser("""{"numbers": [1,23,3], "string": "string"}""")
	# assert p.parse_entity isa JsonObject
	# ~~~
	fun parse_entity: nullable Serializable do
		var srclen = len
		ignore_whitespaces
		if pos >= srclen then return make_parse_error("Empty JSON")
		var c = src[pos]
		if c == '[' then
			pos += 1
			return parse_json_array
		else if c == '"' then
			var s = parse_json_string
			return s
		else if c == '{' then
			pos += 1
			return parse_json_object
		else if c == 'f' then
			if pos + 4 >= srclen then make_parse_error("Error: bad JSON entity")
			if src[pos + 1] == 'a' and src[pos + 2] == 'l' and src[pos + 3] == 's' and src[pos + 4] == 'e' then
				pos += 5
				return false
			end
			return make_parse_error("Error: bad JSON entity")
		else if c == 't' then
			if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
			if src[pos + 1] == 'r' and src[pos + 2] == 'u' and src[pos + 3] == 'e' then
				pos += 4
				return true
			end
			return make_parse_error("Error: bad JSON entity")
		else if c == 'n' then
			if pos + 3 >= srclen then make_parse_error("Error: bad JSON entity")
			if src[pos + 1] == 'u' and src[pos + 2] == 'l' and src[pos + 3] == 'l' then
				pos += 4
				return null
			end
			return make_parse_error("Error: bad JSON entity")
		end
		if not c.is_json_num_start then return make_parse_error("Bad JSON character")
		return parse_json_number
	end

	# Parses a JSON Array
	fun parse_json_array: Serializable do
		var max = len
		if pos >= max then return make_parse_error("Incomplete JSON array")
		var arr = new JsonArray
		var c = src[pos]
		while not c == ']' do
			ignore_whitespaces
			if pos >= max then return make_parse_error("Incomplete JSON array")
			if src[pos] == ']' then break
			var ent = parse_entity
			#print "Parsed an entity {ent} for a JSON array"
			if ent isa JsonParseError then return ent
			arr.add ent
			ignore_whitespaces
			if pos >= max then return make_parse_error("Incomplete JSON array")
			c = src[pos]
			if c == ']' then break
			if c != ',' then return make_parse_error("Bad array separator {c}")
			pos += 1
		end
		pos += 1
		return arr
	end

	# Parses a JSON Object
	fun parse_json_object: Serializable do
		var max = len
		if pos >= max then return make_parse_error("Incomplete JSON object")
		var obj = new JsonObject
		var c = src[pos]
		while not c == '}' do
			ignore_whitespaces
			if pos >= max then return make_parse_error("Malformed JSON object")
			if src[pos] == '}' then break
			var key = parse_entity
			#print "Parsed key {key} for JSON object"
			if not key isa String then return make_parse_error("Bad key format {key or else "null"}")
			ignore_whitespaces
			if pos >= max then return make_parse_error("Incomplete JSON object")
			if not src[pos] == ':' then return make_parse_error("Bad key/value separator {src[pos]}")
			pos += 1
			ignore_whitespaces
			var value = parse_entity
			#print "Parsed value {value} for JSON object"
			if value isa JsonParseError then return value
			obj[key] = value
			ignore_whitespaces
			if pos >= max then return make_parse_error("Incomplete JSON object")
			c = src[pos]
			if c == '}' then break
			if c != ',' then return make_parse_error("Bad object separator {src[pos]}")
			pos += 1
		end
		pos += 1
		return obj
	end

	# Creates a `JsonParseError` with the right message and location
	protected fun make_parse_error(message: String): JsonParseError do
		var err = new JsonParseError(message)
		err.location = hot_location
		return err
	end

	# Parses an Int or Float
	fun parse_json_number: Serializable do
		var max = len
		var p = pos
		var c = src[p]
		var is_neg = false
		if c == '-' then
			is_neg = true
			p += 1
			if p >= max then return make_parse_error("Bad JSON number")
			c = src[p]
		end
		var val = 0
		while c.is_numeric do
			val *= 10
			val += c.to_i
			p += 1
			if p >= max then break
			c = src[p]
		end
		if c == '.' then
			p += 1
			if p >= max then return make_parse_error("Bad JSON number")
			c = src[p]
			var fl = val.to_f
			var frac = 0.1
			while c.is_numeric do
				fl += c.to_i.to_f * frac
				frac /= 10.0
				p += 1
				if p >= max then break
				c = src[p]
			end
			if c == 'e' or c == 'E' then
				p += 1
				var exp = 0
				if p >= max then return make_parse_error("Malformed JSON number")
				c = src[p]
				while c.is_numeric do
					exp *= 10
					exp += c.to_i
					p += 1
					if p >= max then break
					c = src[p]
				end
				fl *= (10 ** exp).to_f
			end
			if p < max and not c.is_json_separator then return make_parse_error("Malformed JSON number")
			pos = p
			if is_neg then return -fl
			return fl
		end
		if c == 'e' or c == 'E' then
			p += 1
			if p >= max then return make_parse_error("Bad JSON number")
			var exp = src[p].to_i
			c = src[p]
			while c.is_numeric do
				exp *= 10
				exp += c.to_i
				p += 1
				if p >= max then break
				c = src[p]
			end
			val *= (10 ** exp)
		end
		if p < max and not src[p].is_json_separator then return make_parse_error("Malformed JSON number")
		pos = p
		if is_neg then return -val
		return val
	end

	private var parse_str_buf = new FlatBuffer

	# Parses and returns a Nit string from a JSON String
	fun parse_json_string: Serializable do
		var src = src
		var ln = src.length
		var p = pos
		p += 1
		if p > ln then return make_parse_error("Malformed JSON String")
		var c = src[p]
		var ret = parse_str_buf
		var chunk_st = p
		while c != '"' do
			if c != '\\' then
				p += 1
				if p >= ln then return make_parse_error("Malformed JSON string")
				c = src[p]
				continue
			end
			ret.append_substring_impl(src, chunk_st, p - chunk_st)
			p += 1
			if p >= ln then return make_parse_error("Malformed Escape sequence in JSON string")
			c = src[p]
			if c == 'r' then
				ret.add '\r'
				p += 1
			else if c == 'n' then
				ret.add '\n'
				p += 1
			else if c == 't' then
				ret.add '\t'
				p += 1
			else if c == 'u' then
				var cp = 0
				p += 1
				for i in [0 .. 4[ do
					cp <<= 4
					if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
					c = src[p]
					if c >= '0' and c <= '9' then
						cp += c.code_point - '0'.code_point
					else if c >= 'a' and c <= 'f' then
						cp += c.code_point - 'a'.code_point + 10
					else if c >= 'A' and c <= 'F' then
						cp += c.code_point - 'A'.code_point + 10
					else
						make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
					end
					p += 1
				end
				c = cp.code_point
				if cp >= 0xD800 and cp <= 0xDBFF then
					if p >= ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
					c = src[p]
					if c != '\\' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
					p += 1
					c = src[p]
					if c != 'u' then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
					var locp = 0
					p += 1
					for i in [0 .. 4[ do
						locp <<= 4
						if p > ln then make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
						c = src[p]
						if c >= '0' and c <= '9' then
							locp += c.code_point - '0'.code_point
						else if c >= 'a' and c <= 'f' then
							locp += c.code_point - 'a'.code_point + 10
						else if c >= 'A' and c <= 'F' then
							locp += c.code_point - 'A'.code_point + 10
						else
							make_parse_error("Malformed \uXXXX Escape sequence in JSON string")
						end
						p += 1
					end
					c = (((locp & 0x3FF) | ((cp & 0x3FF) << 10)) + 0x10000).code_point
				end
				ret.add c
			else if c == 'b' then
				ret.add 8.code_point
				p += 1
			else if c == 'f' then
				ret.add '\f'
				p += 1
			else
				p += 1
				ret.add c
			end
			chunk_st = p
			c = src[p]
		end
		pos = p + 1
		if ret.is_empty then return src.substring(chunk_st, p - chunk_st)
		ret.append_substring_impl(src, chunk_st, p - chunk_st)
		var rets = ret.to_s
		ret.clear
		return rets
	end

	# Ignores any character until a JSON separator is encountered
	fun ignore_until_separator do
		var max = len
		while pos < max do
			if not src[pos].is_json_separator then return
		end
	end
end

# A map that can be translated into a JSON object.
interface JsonMapRead[K: String, V: nullable Serializable]
	super MapRead[K, V]
	super Serializable
end

# A JSON Object.
class JsonObject
	super JsonMapRead[String, nullable Serializable]
	super HashMap[String, nullable Serializable]
end

# A sequence that can be translated into a JSON array.
class JsonSequenceRead[E: nullable Serializable]
	super Serializable
	super SequenceRead[E]
end

# A JSON array.
class JsonArray
	super JsonSequenceRead[nullable Serializable]
	super Array[nullable Serializable]
end
lib/json/static.nit:19,1--480,3