Some cleaning for the markdown library.
The moved method `token_at` will be used in a further PR.
Pull-Request: #920
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
redef class Object
# Factorize cast
- fun json_as_a: Array[nullable Object] do return self.as(Array[nullable Object])
+ fun json_as_a: JsonArray do return self.as(JsonArray)
# Factorize cast
- fun json_as_map: Map[String, nullable Object] do return self.as(Map[String, nullable Object])
+ fun json_as_map: JsonObject do return self.as(JsonObject)
end
redef class GithubCurl
# Get a given pull request (PR)
- fun getpr(number: Int): Map[String, nullable Object]
+ fun getpr(number: Int): JsonObject
do
var pr = get_and_check("https://api.github.com/repos/privat/nit/pulls/{number}")
var prm = pr.json_as_map
end
# Get reviewers of a PR
- fun getrev(pr: Map[String, nullable Object]): Array[String]
+ fun getrev(pr: JsonObject): Array[String]
do
var number = pr["number"].as(Int)
var user = pr["user"].json_as_map["login"].as(String)
import github_api
-# The proprieties introduced by this redef are to be used only on HashMap
+# The proprieties introduced by this redef are to be used only on a JSON object
# representing a Github repository.
-redef class HashMap[K, V]
+redef class JsonObject
# The repository has at least 50% Java code
fun has_lots_of_java: Bool
do
- assert self isa HashMap[String, nullable Object]
var java_count = 0
if keys.has("Java") then java_count = self["Java"].as(Int)
# The repository has at least 100 lines of C code
fun has_some_c: Bool
do
- assert self isa HashMap[String, nullable Object]
var c_count = 0
if keys.has("C") then c_count = self["C"].as(Int)
return c_count > 100
loop
# Get a page of the main query
var uri = "https://api.github.com/search/repositories?q={main_query}&page={page}&per_page={per_page}&sort=stars"
- var obj = curl.get_and_check(uri).as(HashMap[String, nullable Object])
+ var obj = curl.get_and_check(uri).as(JsonObject)
# Main object has "total_count" and "items"
- var items = obj["items"].as(Array[nullable Object])
+ var items = obj["items"].as(JsonArray)
# "items" is an array of Json objects
for item in items do
- assert item isa HashMap[String, nullable Object]
+ assert item isa JsonObject
# Each item has "name" and "languages_url"
assert item.keys.has("name")
# Download the language list
var lang_url = item["languages_url"].as(String)
- var langs = curl.get_and_check(lang_url).as(HashMap[String, nullable Object])
+ var langs = curl.get_and_check(lang_url).as(JsonObject)
# The project is of interest if it has lots of Java and at least some C
var may_be_of_interest = langs.has_lots_of_java and langs.has_some_c
exp = e d+;
e = ('e'|'E') ('+'|'-')?;
-string = '"' (Any-'\\'-'"' | '\\'Any)* '"';
+hexdig = '0'..'9' | 'a'..'z' | 'A'..'Z';
+string = '"' (Any - '\\' - '"' | '\\' (
+ '\\' |
+ '"' |
+ '/' |
+ 'b' |
+ 'f' |
+ 'n' |
+ 'r' |
+ 't' |
+ 'u' hexdig hexdig hexdig hexdig
+ ))* '"';
blank = (' '|'\n'|'\t')+;
# A counter counts occurrences of things
# Use this instead of a `HashMap[E, Int]`
+#
+# ~~~
+# var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+# assert c["a"] == 2
+# assert c["b"] == 3
+# assert c["c"] == 1
+# assert c["d"] == 0
+# ~~~
+#
+# The counter class can also be used to gather statistical informations.
+#
+# ~~~~
+# assert c.length == 3 # because 3 distinct values
+# assert c.max == "b" # because "b" has the most count (3)
+# assert c.avg == 2.0 # because it is the mean of the counts
+# ~~~~
class Counter[E: Object]
super Map[E, Int]
# Total number of counted occurrences
+ #
+ # ~~~
+ # var c = new Counter[String]
+ # assert c.sum == 0
+ # c.inc_all(["a", "a", "b", "b", "b", "c"])
+ # assert c.sum == 6
+ # ~~~
var sum: Int = 0
private var map = new HashMap[E, Int]
sum += 1
end
+ # Count one more for each element of `es`
+ fun inc_all(es: Collection[E])
+ do
+ for e in es do inc(e)
+ end
+
+ # A new Counter initialized with `inc_all`.
+ init from(es: Collection[E])
+ do
+ inc_all(es)
+ end
+
# Return an array of elements sorted by occurrences
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.sort == ["c", "a", "b"]
+ # ~~~
fun sort: Array[E]
do
var res = map.keys.to_a
# @toimplement by default just call `to_s` on the element
protected fun element_to_s(e: E): String
do
- do return e.to_s
+ return e.to_s
end
# Display statistical information
end
end
- # Return the element with the highest value
+ # Return the element with the highest value (aka. the mode)
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.max == "b"
+ # ~~~
+ #
+ # If more than one max exists, the first one is returned.
fun max: nullable E do
var max: nullable Int = null
var elem: nullable E = null
end
# Return the couple with the lowest value
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.min == "c"
+ # ~~~
+ #
+ # If more than one min exists, the first one is returned.
fun min: nullable E do
var min: nullable Int = null
var elem: nullable E = null
return elem
end
- # Values average
+ # Values average (aka. arithmetic mean)
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.avg == 2.0
+ # ~~~
fun avg: Float do
if values.is_empty then return 0.0
return (sum / values.length).to_f
end
# The standard derivation of the counter values
+ #
+ # ~~~
+ # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"])
+ # assert c.std_dev > 0.81
+ # assert c.std_dev < 0.82
+ # ~~~
fun std_dev: Float do
var avg = self.avg
var sum = 0.0
# Get the requested URI, and check the HTTP response. Then convert to JSON
# and check for Github errors.
- fun get_and_check(uri: String): nullable Object
+ fun get_and_check(uri: String): nullable Jsonable
do
var request = new CurlHTTPRequest(uri, self)
request.user_agent = user_agent
var response = request.execute
if response isa CurlResponseSuccess then
- var obj = response.body_str.json_to_nit_object
- if obj isa HashMap[String, nullable Object] then
+ var obj = response.body_str.parse_json
+ if obj isa JsonObject then
if obj.keys.has("message") then
print "Message from Github API: {obj["message"] or else ""}"
print "Requested URI: {uri}"
# to get the underlying Json data. It can also be used as any Json types.
module dynamic
+import error
private import static
-import standard
class JsonValue
var value: nullable Object
#
# assert """{"a": 123}""".to_json_value.is_map
# assert not "123".to_json_value.is_map
- fun is_map: Bool do return value isa HashMap[String, nullable Object]
+ fun is_map: Bool do return value isa MapRead[String, nullable Object]
# Get this value as a `Map[String, JsonValue]`
#
# require: `self.is_map`
fun to_map: Map[String, JsonValue] do
var value = value
- assert value isa HashMap[String, nullable Object]
+ assert value isa MapRead[String, nullable Object]
var map = new HashMap[String, JsonValue]
for k, v in value do map[k] = new JsonValue(v)
# assert "[1, 2, 3, 4, 5]".to_json_value.is_array
# assert "[null, true, false, 0.0, 1, \"str\"]".to_json_value.is_array
# assert """["a", "b", "c"]""".to_json_value.is_array
- fun is_array: Bool do return value isa Array[nullable Object]
+ fun is_array: Bool do return value isa SequenceRead[nullable Object]
# Get this value as an `Array[JsonValue]`
#
fun to_a: Array[JsonValue]
do
var value = value
- assert value isa Array[nullable Object]
+ assert value isa SequenceRead[nullable Object]
var a = new Array[JsonValue]
for e in value do a.add(new JsonValue(e))
return a
end
+ ### Error
+
+ # Is this value an error?
+ #
+ # assert "[]".to_json_value[0].is_error
+ # assert "[".to_json_value.is_error
+ # assert not "[]".to_json_value.is_error
+ fun is_error: Bool do return value isa Error
+
+ # Get this value as a `Error`.
+ #
+ # require: `self.is_error`
+ fun to_error: Error do return value.as(Error)
+
+ ### JsonParseError
+
+ # Is this value a parse error?
+ #
+ # assert "[".to_json_value.is_parse_error
+ # assert not "[]".to_json_value.is_parse_error
+ fun is_parse_error: Bool do return value isa JsonParseError
+
+ # Get this value as a `JsonParseError`.
+ #
+ # require: `self.is_parse_error`
+ fun to_parse_error: JsonParseError do return value.as(JsonParseError)
+
+ ### Children access
+
# Iterator over the values of the array `self`
#
# require: `self.is_array`
# assert """{"a": 123}""".to_json_value["a"].to_i == 123
# assert """{"123": "a"}""".to_json_value[123].to_s == "a"
# assert """{"John Smith": 1980}""".to_json_value[["John ", "Smith"]].to_i == 1980
+ # assert """{"a": 123}""".to_json_value["b"].is_error
#
# assert """["a", "b", "c"]""".to_json_value[0].to_s == "a"
- fun [](key: Object): JsonValue
- do
+ # assert """["a", "b", "c"]""".to_json_value[3].is_error
+ fun [](key: Object): JsonValue do
var value = value
- if value isa HashMap[String, nullable Object] then
- return new JsonValue(value[key.to_s])
- else if value isa Array[nullable Object] then
- assert key isa Int
- return new JsonValue(value[key])
- else abort
+ var result: nullable Object
+ if is_error then
+ return self
+ else if value isa MapRead[String, nullable Object] then
+ key = key.to_s
+ if value.has_key(key) then
+ result = value[key]
+ else
+ result = new JsonKeyError("Key `{key}` not found.", self, key)
+ end
+ else if value isa SequenceRead[nullable Object] then
+ if key isa Int then
+ if key < value.length and key >= 0 then
+ result = value[key]
+ else
+ result = new JsonKeyError("Index `{key}` out of bounds.",
+ self, key)
+ end
+ else
+ result = new JsonKeyError("Invalid key type. Expecting `Int`. Got `{key.class_name}`.",
+ self, key)
+ end
+ else
+ result = new JsonKeyError("Invalid `[]` access on a `{json_type}` JsonValue.",
+ self, key)
+ end
+ return new JsonValue(result)
end
# Advanced query to get a value within the map `self` or it's children.
# assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a").is_map
# assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a.t").to_bool
# assert not """{"a": {"t": true, "f": false}}""".to_json_value.get("a.f").to_bool
+ # assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a.t.t").is_error
# assert """{"a": {"b": {"c": {"d": 123}}}}""".to_json_value.get("a.b.c.d").to_i == 123
- fun get(query: String): JsonValue
- do
+ # assert """{"a": {"b": {"c": {"d": 123}}}}""".to_json_value.get("a.z.c.d").is_error
+ fun get(query: String): JsonValue do
var keys = query.split(".")
var value = value
- for key in keys do
- assert value isa HashMap[String, nullable Object]
- value = value[key]
+ if is_error then return self
+ for i in [0..keys.length[ do
+ var key = keys[i]
+ if value isa MapRead[String, nullable Object] then
+ if value.has_key(key) then
+ value = value[key]
+ else
+ var sub_query = sub_query_to_s(keys, i)
+ var e = new JsonKeyError("Key `{key}` not found.",
+ self, sub_query)
+ return new JsonValue(e)
+ end
+ else
+ var sub_query = sub_query_to_s(keys, i)
+ var val_type = (new JsonValue(value)).json_type
+ var e = new JsonKeyError("Value at `{sub_query}` is not a map. Got type `{val_type}`",
+ self, sub_query)
+ return new JsonValue(e)
+ end
end
return new JsonValue(value)
end
+
+ # Concatenate all keys up to `last` for debugging purposes.
+ #
+ # Note: This method deletes elements in `keys`.
+ private fun sub_query_to_s(keys: Array[String], last: Int): String do
+ last += 1
+ for j in [last..keys.length[ do keys.pop
+ return keys.join(".")
+ end
+
+ # Return a human-readable description of the type.
+ #
+ # For debugging purpose only.
+ fun json_type: String do
+ if is_array then return "array"
+ if is_bool then return "bool"
+ if is_float then return "float"
+ if is_int then return "int"
+ if is_null then return "null"
+ if is_map then return "map"
+ if is_string then return "string"
+ if is_parse_error then return "parse_error"
+ if is_error then return "error"
+ return "undefined"
+ end
end
-redef class String
+# Keyed access failed.
+class JsonKeyError
+ super Error
+
+ # The value on which the access was requested.
+ var json_value: JsonValue
+
+ # The requested key.
+ #
+ # In the case of `JsonValue.get`, the sub-query that failed.
+ var key: Object
+end
+
+redef class Text
# Parse `self` to obtain a `JsonValue`
- fun to_json_value: JsonValue
- do
- var value = json_to_nit_object
+ fun to_json_value: JsonValue do
+ var value = parse_json
return new JsonValue(value)
end
end
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Errors related to JSON parsing.
+module json::error
+
+import nitcc_runtime
+
+# Ill-formed JSON.
+class JsonParseError
+ super Error
+
+ # The location of the error in the original text.
+ var position: nullable Position
+
+ redef fun to_s do
+ var p = position
+ if p isa Position then
+ return "[{p}] {super}"
+ else
+ return super
+ end
+ end
+end
# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# See the License for the specific language governing permissions and
# limitations under the License.
-# Offers two APIs to manipulate read Json strings.
+# Provides two APIs to manipulate JSON strings.
+#
+# Both `dynamic` and `static` modules provide at least a method to parse a
+# value written in JSON, but only `static` provide a method to translate a
+# value into JSON.
#
# The `dynamic` module provides a simple interface to get information
-# from a Json document. You must be careful as all services are provided on
+# from a JSON document. You must be careful as all services are provided on
# each nodes and a wrongful use can `abort`.
#
-# The `static` module converts a Json string to a nullable Nit object. The object
-# must then be type checked before it can be used.
+# The `static` module provides a common interface for anything that can be
+# translated into a JSON document. The provided parsing method return a
+# nullable Nit object that must then be type-checked before it can be used.
module json
import static
private fun dfastate_28: DFAState28 do return once new DFAState28
private fun dfastate_29: DFAState29 do return once new DFAState29
private fun dfastate_30: DFAState30 do return once new DFAState30
+ private fun dfastate_31: DFAState31 do return once new DFAState31
+ private fun dfastate_32: DFAState32 do return once new DFAState32
+ private fun dfastate_33: DFAState33 do return once new DFAState33
+ private fun dfastate_34: DFAState34 do return once new DFAState34
end
class MyNToken
super NToken
super DFAState
redef fun trans(char) do
var c = char.ascii
- return dfastate_2
+ if c <= 33 then return null
+ if c <= 34 then return dfastate_2
+ if c <= 46 then return null
+ if c <= 47 then return dfastate_2
+ if c <= 91 then return null
+ if c <= 92 then return dfastate_2
+ if c <= 97 then return null
+ if c <= 98 then return dfastate_2
+ if c <= 101 then return null
+ if c <= 102 then return dfastate_2
+ if c <= 109 then return null
+ if c <= 110 then return dfastate_2
+ if c <= 113 then return null
+ if c <= 114 then return dfastate_2
+ if c <= 115 then return null
+ if c <= 116 then return dfastate_2
+ if c <= 117 then return dfastate_31
+ return null
+ end
+end
+private class DFAState31
+ super DFAState
+ redef fun trans(char) do
+ var c = char.ascii
+ if c <= 47 then return null
+ if c <= 57 then return dfastate_32
+ if c <= 64 then return null
+ if c <= 90 then return dfastate_32
+ if c <= 96 then return null
+ if c <= 122 then return dfastate_32
+ return null
+ end
+end
+private class DFAState32
+ super DFAState
+ redef fun trans(char) do
+ var c = char.ascii
+ if c <= 47 then return null
+ if c <= 57 then return dfastate_33
+ if c <= 64 then return null
+ if c <= 90 then return dfastate_33
+ if c <= 96 then return null
+ if c <= 122 then return dfastate_33
+ return null
+ end
+end
+private class DFAState33
+ super DFAState
+ redef fun trans(char) do
+ var c = char.ascii
+ if c <= 47 then return null
+ if c <= 57 then return dfastate_34
+ if c <= 64 then return null
+ if c <= 90 then return dfastate_34
+ if c <= 96 then return null
+ if c <= 122 then return dfastate_34
+ return null
+ end
+end
+private class DFAState34
+ super DFAState
+ redef fun trans(char) do
+ var c = char.ascii
+ if c <= 47 then return null
+ if c <= 57 then return dfastate_2
+ if c <= 64 then return null
+ if c <= 90 then return dfastate_2
+ if c <= 96 then return null
+ if c <= 122 then return dfastate_2
+ return null
end
end
# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-concept.com>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# Static interface to get Nit objects from a Json string.
#
-# `String::json_to_nit_object` returns an equivalent Nit object from
+# `Text::parse_json` returns an equivalent Nit object from
# the Json source. This object can then be type checked by the usual
# languages features (`isa` and `as`).
module static
-import standard
+import error
private import json_parser
private import json_lexer
+# Something that can be translated to JSON.
+interface Jsonable
+ # Encode `self` in JSON.
+ #
+ # SEE: `append_json`
+ fun to_json: String is abstract
+
+ # Append the JSON representation of `self` to the specified buffer.
+ #
+ # SEE: `to_json`
+ fun append_json(buffer: Buffer) do
+ buffer.append(to_json)
+ end
+end
+
+redef class Text
+ super Jsonable
+
+ redef fun append_json(buffer) do
+ buffer.add '\"'
+ for i in [0..self.length[ do
+ var char = self[i]
+ if char == '\\' then
+ buffer.append "\\\\"
+ else if char == '\"' then
+ buffer.append "\\\""
+ else if char == '\/' then
+ buffer.append "\\/"
+ else if char < 16.ascii then
+ if char == '\n' then
+ buffer.append "\\n"
+ else if char == '\r' then
+ buffer.append "\\r"
+ else if char == '\t' then
+ buffer.append "\\t"
+ else if char == 0x0C.ascii then
+ buffer.append "\\f"
+ else if char == 0x08.ascii then
+ buffer.append "\\b"
+ else
+ buffer.append "\\u000{char.ascii.to_hex}"
+ end
+ else if char < ' ' then
+ buffer.append "\\u00{char.ascii.to_hex}"
+ else
+ buffer.add char
+ end
+ end
+ buffer.add '\"'
+ end
+
+ # Encode `self` in JSON.
+ #
+ # assert "\t\"http://example.com\"\r\n\0\\".to_json ==
+ # "\"\\t\\\"http:\\/\\/example.com\\\"\\r\\n\\u0000\\\\\""
+ redef fun to_json do
+ var buffer = new FlatBuffer
+ append_json(buffer)
+ return buffer.write_to_string
+ 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 syntaxic error:
+ #
+ # var bad = "\{foo: \"bar\"\}".parse_json
+ # assert bad isa JsonParseError
+ # assert bad.position.col_start == 2
+ fun parse_json: nullable Jsonable do
+ var lexer = new Lexer_json(to_s)
+ var parser = new Parser_json
+ var tokens = lexer.lex
+ parser.tokens.add_all(tokens)
+ var root_node = parser.parse
+ if root_node isa NStart then
+ return root_node.n_0.to_nit_object
+ else if root_node isa NError then
+ return new JsonParseError(root_node.message, root_node.position)
+ else abort
+ end
+end
+
+redef class Buffer
+
+ # Append the JSON representation of `jsonable` to `self`.
+ #
+ # Append `"null"` for `null`.
+ private fun append_json_of(jsonable: nullable Jsonable) do
+ if jsonable isa Jsonable then
+ append jsonable.to_json
+ else
+ append "null"
+ end
+ end
+end
+
+redef class Int
+ super Jsonable
+
+ # Encode `self` in JSON.
+ #
+ # assert 0.to_json == "0"
+ # assert (-42).to_json == "-42"
+ redef fun to_json do return self.to_s
+end
+
+redef class Float
+ super Jsonable
+
+ # Encode `self` in JSON.
+ #
+ # Note: Because this method use `to_s`, it may lose precision.
+ #
+ # ~~~
+ # # Will not work as expected.
+ # # assert (-0.0).to_json == "-0.0"
+ #
+ # assert (.5).to_json == "0.5"
+ # assert (0.0).to_json == "0.0"
+ # ~~~
+ redef fun to_json do return self.to_s
+end
+
+redef class Bool
+ super Jsonable
+
+ # Encode `self` in JSON.
+ #
+ # assert true.to_json == "true"
+ # assert false.to_json == "false"
+ redef fun to_json do return self.to_s
+end
+
+# A map that can be translated into a JSON object.
+interface JsonMapRead[K: String, V: nullable Jsonable]
+ super MapRead[K, V]
+ super Jsonable
+
+ redef fun append_json(buffer) do
+ buffer.append "\{"
+ var it = iterator
+ if it.is_ok then
+ append_json_entry(it, buffer)
+ while it.is_ok do
+ buffer.append ","
+ append_json_entry(it, buffer)
+ end
+ end
+ it.finish
+ buffer.append "\}"
+ end
+
+ # Encode `self` in JSON.
+ #
+ # var obj = new JsonObject
+ # obj["foo"] = "bar"
+ # assert obj.to_json == "\{\"foo\":\"bar\"\}"
+ # obj = new JsonObject
+ # obj["baz"] = null
+ # assert obj.to_json == "\{\"baz\":null\}"
+ redef fun to_json do
+ var buffer = new FlatBuffer
+ append_json(buffer)
+ return buffer.write_to_string
+ end
+
+ private fun append_json_entry(iterator: MapIterator[String, nullable Jsonable],
+ buffer: Buffer) do
+ buffer.append iterator.key.to_json
+ buffer.append ":"
+ buffer.append_json_of(iterator.item)
+ iterator.next
+ end
+end
+
+# A JSON Object.
+class JsonObject
+ super JsonMapRead[String, nullable Jsonable]
+ super HashMap[String, nullable Jsonable]
+end
+
+# A sequence that can be translated into a JSON array.
+class JsonSequenceRead[E: nullable Jsonable]
+ super Jsonable
+ super SequenceRead[E]
+
+ redef fun append_json(buffer) do
+ buffer.append "["
+ var it = iterator
+ if it.is_ok then
+ append_json_entry(it, buffer)
+ while it.is_ok do
+ buffer.append ","
+ append_json_entry(it, buffer)
+ end
+ end
+ it.finish
+ buffer.append "]"
+ end
+
+ # Encode `self` in JSON.
+ #
+ # var arr = new JsonArray.with_items("foo", null)
+ # assert arr.to_json == "[\"foo\",null]"
+ # arr.pop
+ # assert arr.to_json =="[\"foo\"]"
+ # arr.pop
+ # assert arr.to_json =="[]"
+ redef fun to_json do
+ var buffer = new FlatBuffer
+ append_json(buffer)
+ return buffer.write_to_string
+ end
+
+ private fun append_json_entry(iterator: Iterator[nullable Jsonable],
+ buffer: Buffer) do
+ buffer.append_json_of(iterator.item)
+ iterator.next
+ end
+end
+
+# A JSON array.
+class JsonArray
+ super JsonSequenceRead[nullable Jsonable]
+ super Array[nullable Jsonable]
+end
+
+redef class JsonParseError
+ super Jsonable
+
+ # Get the JSON representation of `self`.
+ #
+ # var err = new JsonParseError("foo", new Position(1, 2, 3, 4, 5, 6))
+ # assert err.to_json == "\{\"error\":\"JsonParseError\"," +
+ # "\"position\":\{" +
+ # "\"pos_start\":1,\"pos_end\":2," +
+ # "\"line_start\":3,\"line_end\":4," +
+ # "\"col_start\":5,\"col_end\":6" +
+ # "\},\"message\":\"foo\"\}"
+ redef fun to_json do
+ return "\{\"error\":\"JsonParseError\"," +
+ "\"position\":{position.to_json}," +
+ "\"message\":{message.to_json}\}"
+ end
+end
+
+redef class Position
+ super Jsonable
+
+ # Get the JSON representation of `self`.
+ #
+ # var pos = new Position(1, 2, 3, 4, 5, 6)
+ # assert pos.to_json == "\{" +
+ # "\"pos_start\":1,\"pos_end\":2," +
+ # "\"line_start\":3,\"line_end\":4," +
+ # "\"col_start\":5,\"col_end\":6" +
+ # "\}"
+ redef fun to_json do
+ return "\{\"pos_start\":{pos_start},\"pos_end\":{pos_end}," +
+ "\"line_start\":{line_start},\"line_end\":{line_end}," +
+ "\"col_start\":{col_start},\"col_end\":{col_end}\}"
+ end
+end
+
+################################################################################
+# Redef parser
+
redef class Nvalue
- fun to_nit_object: nullable Object is abstract
+ fun to_nit_object: nullable Jsonable is abstract
end
redef class Nvalue_number
end
redef class Nstring
- # FIXME support \n, etc.
- fun to_nit_string: String do return text.substring(1, text.length-2).
- replace("\\\\", "\\").replace("\\\"", "\"").replace("\\b", "\b").
- replace("\\/", "/").replace("\\n", "\n").replace("\\r", "\r").
- replace("\\t", "\t")
+ fun to_nit_string: String do
+ var res = new FlatBuffer
+ var i = 1
+ while i < text.length - 1 do
+ var char = text[i]
+ if char == '\\' then
+ i += 1
+ char = text[i]
+ if char == 'b' then
+ char = 0x08.ascii
+ else if char == 'f' then
+ char = 0x0C.ascii
+ 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 code = text.substring(i + 1, 4).to_hex
+ # TODO UTF-16 escaping is not supported yet.
+ if code >= 128 then
+ char = '?'
+ else
+ char = code.ascii
+ end
+ i += 4
+ end
+ # `"`, `/` or `\` => Keep `char` as-is.
+ end
+ res.add char
+ i += 1
+ end
+ return res.write_to_string
+ end
end
redef class Nvalue_object
- redef fun to_nit_object
- do
- var obj = new HashMap[String, nullable Object]
+ redef fun to_nit_object do
+ var obj = new JsonObject
var members = n_members
if members != null then
var pairs = members.pairs
redef class Npair
fun name: String do return n_string.to_nit_string
- fun value: nullable Object do return n_value.to_nit_object
+ fun value: nullable Jsonable do return n_value.to_nit_object
end
redef class Nvalue_array
redef fun to_nit_object
do
- var arr = new Array[nullable Object]
+ var arr = new JsonArray
var elements = n_elements
if elements != null then
var items = elements.items
redef class Nelements_head
redef fun items do return [n_value]
end
-
-redef class Text
- fun json_to_nit_object: nullable Object
- do
- var lexer = new Lexer_json(to_s)
- var parser = new Parser_json
- var tokens = lexer.lex
- parser.tokens.add_all(tokens)
- var root_node = parser.parse
- if root_node isa NStart then
- return root_node.n_0.to_nit_object
- else if root_node isa NLexerError then
- var pos = root_node.position
- print "Json lexer error: {root_node.message} at {pos or else "<unknown>"} for {root_node}"
- return null
- else if root_node isa NParserError then
- var pos = root_node.position
- print "Json parsing error: {root_node.message} at {pos or else "<unknown>"} for {root_node}"
- return null
- else abort
- end
-end
class JsonDeserializer
super Deserializer
- var root: nullable Object
- var path = new Array[HashMap[String, nullable Object]]
+ var root: nullable Jsonable
+ var path = new Array[JsonObject]
var id_to_object = new HashMap[Int, Object]
var just_opened_id: nullable Int = null
init(text: Text)
do
- var root = text.json_to_nit_object
- if root isa HashMap[String, nullable Object] then path.add(root)
+ var root = text.parse_json
+ if root isa JsonObject then path.add(root)
self.root = root
end
# Convert from simple Json object to Nit object
private fun convert_object(object: nullable Object): nullable Object
do
- if object isa HashMap[String, nullable Object] then
+ if object isa JsonObject then
assert object.keys.has("__kind")
var kind = object["__kind"]
end
redef class Char
- redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": \"{to_s.to_json_s}\"\}"
+ redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
end
redef class String
- redef fun serialize_to_json(v) do v.stream.write("\"{to_json_s}\"")
-
- private fun to_json_s: String do return self.replace("\\", "\\\\").
- replace("\"", "\\\"").replace("/", "\\/").
- replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t")
- # FIXME add support for unicode char when supported by Nit strings
- # FIXME add support for \f! # .replace("\f", "\\f")
- # FIXME add support for \b .replace("\b", "\\b")
+ redef fun serialize_to_json(v) do v.stream.write(to_json)
end
redef class NativeString
v.notify_of_creation self
var length = v.deserialize_attribute("__length").as(Int)
- var arr = v.path.last["__items"].as(Array[nullable Object])
+ var arr = v.path.last["__items"].as(SequenceRead[nullable Object])
for i in length.times do
var obj = v.convert_object(arr[i])
self.add obj
# cURL requests compatible with the JSON REST APIs.
module curl_json
-import jsonable
+import json::static
intrude import curl
# An abstract request that defines most of the standard options for Neo4j REST API
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# This file is free software, which comes along with NIT. This software is
+# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. You can modify it is you want, provided this header
+# is kept unaltered, and a notification of the changes is added.
+# You are allowed to redistribute it and sell it, alone or is a part of
+# another product.
+
+# Errors thrown by the `neo4j` library.
+module neo4j::error
+
+import json::static
+
+# An error thrown by the `neo4j` API.
+#
+# var error = new NeoError("ErrorMessage", "ErrorName")
+# assert error.to_json == "\{\"error\":\"ErrorName\",\"message\":\"ErrorMessage\"\}"
+class NeoError
+ super Error
+ super Jsonable
+
+ # The name of the error.
+ #
+ # Used to programmatically distinguish this kind of error from others.
+ var name: String
+
+ redef fun to_json do
+ return "\{\"error\":{name.to_json},\"message\":{message.to_json}\}"
+ end
+
+ redef fun to_s do return "[{name}] {super}"
+end
# end
# assert 2 == graph.nodes.length
init from_json(t: Text) do
- from_json_object(t.to_jsonable.as(JsonObject))
+ from_json_object(t.parse_json.as(JsonObject))
end
# Retrieve the graph from the specified JSON object.
# assert ["foo", "Bar"] == node.labels
# assert 42 == node["baz"]
init from_json(t: Text) do
- from_json_object(t.to_jsonable.as(JsonObject))
+ from_json_object(t.parse_json.as(JsonObject))
end
# Retrieve the node from the specified JSON value.
+++ /dev/null
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Introduce base classes and services for JSON handling.
-module jsonable
-
-import standard
-private import json::json_parser
-private import json::json_lexer
-
-# Something that can be translated to JSON
-interface Jsonable
- # Get the JSON representation of `self`
- fun to_json: String is abstract
-end
-
-redef class String
- super Jsonable
-
- redef fun to_json do
- var res = new FlatBuffer
- res.add '\"'
- for i in [0..self.length[ do
- var char = self[i]
- if char == '\\' then
- res.append("\\\\")
- continue
- else if char == '\"' then
- res.append("\\\"")
- continue
- else if char == '\/' then
- res.append("\\/")
- continue
- else if char == '\n' then
- res.append("\\n")
- continue
- else if char == '\r' then
- res.append("\\r")
- continue
- else if char == '\t' then
- res.append("\\t")
- continue
- end
- res.add char
- end
- res.add '\"'
- return res.write_to_string
- end
-end
-
-redef class Int
- super Jsonable
-
- redef fun to_json do return self.to_s
-end
-
-redef class Float
- super Jsonable
-
- redef fun to_json do return self.to_s
-end
-
-redef class Bool
- super Jsonable
-
- redef fun to_json do return self.to_s
-end
-
-# A JSON Object representation that behaves like a `Map`
-class JsonObject
- super Jsonable
- super Map[String, nullable Jsonable]
-
- private var map = new HashMap[String, nullable Jsonable]
-
- # Create an empty `JsonObject`
- #
- # var obj = new JsonObject
- # assert obj.is_empty
- init do end
-
- # Init the JSON Object from a Nit `Map`
- #
- # var map = new HashMap[String, String]
- # map["foo"] = "bar"
- # map["goo"] = "baz"
- # var obj = new JsonObject.from(map)
- # assert obj.length == 2
- # assert obj["foo"] == "bar"
- # assert obj["goo"] == "baz"
- init from(items: Map[String, nullable Jsonable]) do
- for k, v in items do map[k] = v
- end
-
- redef fun [](key) do return map[key]
- redef fun []=(key, value) do map[key] = value
- redef fun clear do map.clear
- redef fun has_key(key) do return map.has_key(key)
- redef fun is_empty do return map.is_empty
- redef fun iterator do return map.iterator
- redef fun keys do return map.keys
- redef fun values do return map.values
- redef fun length do return map.length
-
- # Advanced query to get a value within `self` or its children.
- #
- # A query is composed of the keys to each object seperated by '.'.
- #
- # REQUIRE `self.has_key(query)`
- #
- # var obj1 = new JsonObject
- # obj1["baz"] = "foobarbaz"
- # var obj2 = new JsonObject
- # obj2["bar"] = obj1
- # var obj3 = new JsonObject
- # obj3["foo"] = obj2
- # assert obj3.get("foo.bar.baz") == "foobarbaz"
- fun get(query: String): nullable Jsonable do
- var keys = query.split(".").reversed
- var key = keys.pop
-
- assert has_key(key)
- var node = self[key]
-
- while not keys.is_empty do
- key = keys.pop
- assert node isa JsonObject and node.has_key(key)
- node = node[key]
- end
- return node
- end
-
- # Create an empty `JsonObject`
- #
- # var obj = new JsonObject
- # obj["foo"] = "bar"
- # assert obj.to_json == "\{\"foo\": \"bar\"\}"
- redef fun to_json do
- var tpl = new Array[String]
- tpl.add "\{"
- var vals = new Array[String]
- for k, v in self do
- if v == null then
- vals.add "{k.to_json}: null"
- else
- vals.add "{k.to_json}: {v.to_json}"
- end
- end
- tpl.add vals.join(",")
- tpl.add "\}"
- return tpl.join("")
- end
-
- redef fun to_s do return to_json
-end
-
-# A JSON Array representation that behaves like a `Sequence`
-class JsonArray
- super Jsonable
- super Sequence[nullable Jsonable]
-
- private var array = new Array[nullable Jsonable]
-
- init do end
-
- # init the JSON Array from a Nit `Collection`
- init from(items: Collection[nullable Jsonable]) do
- array.add_all(items)
- end
-
- redef fun [](key) do return array[key]
- redef fun []=(key, value) do array[key] = value
- redef fun clear do array.clear
- redef fun insert(item, index) do array.insert(item, index)
- redef fun is_empty do return array.is_empty
- redef fun iterator do return array.iterator
- redef fun length do return array.length
- redef fun pop do return array.pop
- redef fun push(value) do array.push(value)
- redef fun remove_at(index) do array.remove_at(index)
- redef fun shift do return array.shift
- redef fun unshift(e) do array.unshift(e)
-
- redef fun to_json do
- var tpl = new Array[String]
- tpl.add "["
- var vals = new Array[String]
- for v in self do
- if v == null then
- vals.add "null"
- else
- vals.add v.to_json
- end
- end
- tpl.add vals.join(",")
- tpl.add "]"
- return tpl.join("")
- end
-
- redef fun to_s do return to_json
-end
-
-# An error in JSON format that can be returned by tools using JSON like parsers.
-#
-# var error = new JsonError("ErrorCode", "ErrorMessage")
-# assert error.to_s == "ErrorCode: ErrorMessage"
-# assert error.to_json == "\{\"error\": \"ErrorCode\", \"message\": \"ErrorMessage\"\}"
-class JsonError
- super Jsonable
-
- # The error code
- var error: String
-
- # The error message
- var message: String
-
- redef fun to_json do
- var tpl = new Array[String]
- tpl.add "\{"
- tpl.add "\"error\": {error.to_json}, "
- tpl.add "\"message\": {message.to_json}"
- tpl.add "\}"
- return tpl.join("")
- end
-
- redef fun to_s do return "{error}: {message}"
-end
-
-# Redef parser
-
-redef class Nvalue
- private fun to_nit_object: nullable Jsonable is abstract
-end
-
-redef class Nvalue_number
- redef fun to_nit_object
- do
- var text = n_number.text
- if text.chars.has('.') or text.chars.has('e') or text.chars.has('E') then return text.to_f
- return text.to_i
- end
-end
-
-redef class Nvalue_string
- redef fun to_nit_object do return n_string.to_nit_string
-end
-
-redef class Nvalue_true
- redef fun to_nit_object do return true
-end
-
-redef class Nvalue_false
- redef fun to_nit_object do return false
-end
-
-redef class Nvalue_null
- redef fun to_nit_object do return null
-end
-
-redef class Nstring
- # FIXME support \n, etc.
- fun to_nit_string: String do
- var res = new FlatBuffer
- var skip = false
- for i in [1..text.length-2] do
- if skip then
- skip = false
- continue
- end
- var char = text[i]
- if char == '\\' and i < text.length - 2 then
- if text[i + 1] == '\\' then
- res.add('\\')
- skip = true
- continue
- end
- if text[i + 1] == '\"' then
- res.add('\"')
- skip = true
- continue
- end
- if text[i + 1] == '/' then
- res.add('\/')
- skip = true
- continue
- end
- if text[i + 1] == 'n' then
- res.add('\n')
- skip = true
- continue
- end
- if text[i + 1] == 'r' then
- res.add('\r')
- skip = true
- continue
- end
- if text[i + 1] == 't' then
- res.add('\t')
- skip = true
- continue
- end
- end
- res.add char
- end
- return res.write_to_string
- end
-end
-
-redef class Nvalue_object
- redef fun to_nit_object
- do
- var obj = new JsonObject
- var members = n_members
- if members != null then
- var pairs = members.pairs
- for pair in pairs do obj[pair.name] = pair.value
- end
- return obj
- end
-end
-
-redef class Nmembers
- fun pairs: Array[Npair] is abstract
-end
-
-redef class Nmembers_tail
- redef fun pairs
- do
- var arr = n_members.pairs
- arr.add n_pair
- return arr
- end
-end
-
-redef class Nmembers_head
- redef fun pairs do return [n_pair]
-end
-
-redef class Npair
- fun name: String do return n_string.to_nit_string
- fun value: nullable Jsonable do return n_value.to_nit_object
-end
-
-redef class Nvalue_array
- redef fun to_nit_object
- do
- var arr = new JsonArray
- var elements = n_elements
- if elements != null then
- var items = elements.items
- for item in items do arr.add(item.to_nit_object)
- end
- return arr
- end
-end
-
-redef class Nelements
- fun items: Array[Nvalue] is abstract
-end
-
-redef class Nelements_tail
- redef fun items
- do
- var items = n_elements.items
- items.add(n_value)
- return items
- end
-end
-
-redef class Nelements_head
- redef fun items do return [n_value]
-end
-
-redef class Text
- # Parse a JSON String as Jsonable entities
- #
- # Example with `JsonObject`"
- #
- # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".to_jsonable
- # assert obj isa JsonObject
- # assert obj["foo"] isa JsonObject
- # assert obj["foo"].as(JsonObject)["bar"] == true
- #
- # Example with `JsonArray`
- #
- # var arr = "[1, 2, 3]".to_jsonable
- # assert arr isa JsonArray
- # assert arr.length == 3
- # assert arr.first == 1
- # assert arr.last == 3
- #
- # Example with `String`
- #
- # var str = "\"foo, bar, baz\"".to_jsonable
- # assert str isa String
- # assert str == "foo, bar, baz"
- #
- # Malformed JSON input returns a `JsonError` object
- #
- # var bad = "\{foo: \"bar\"\}".to_jsonable
- # assert bad isa JsonError
- # assert bad.error == "JsonLexerError"
- fun to_jsonable: nullable Jsonable
- do
- var lexer = new Lexer_json(to_s)
- var parser = new Parser_json
- var tokens = lexer.lex
- parser.tokens.add_all(tokens)
- var root_node = parser.parse
- if root_node isa NStart then
- return root_node.n_0.to_nit_object
- else if root_node isa NLexerError then
- var pos = root_node.position
- var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
- return new JsonError("JsonLexerError", msg)
- else if root_node isa NParserError then
- var pos = root_node.position
- var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
- return new JsonError("JsonParsingError", msg)
- else abort
- end
-end
-
module neo4j
import curl_json
+import error
# Handles Neo4j server start and stop command
#
# Parse the cURL `response` as a JSON string
private fun parse_response(response: CurlResponse): Jsonable do
if response isa CurlResponseSuccess then
- if response.body_str.is_empty then
+ var str = response.body_str
+ if str.is_empty then return new JsonObject
+ var res = str.parse_json
+ if res isa JsonParseError then
+ var e = new NeoError(res.to_s, "JsonParseError")
+ e.cause = res
+ return e
+ end
+ if res == null then
+ # empty response wrap it in empty object
return new JsonObject
- else
- var str = response.body_str
- var res = str.to_jsonable
- if res == null then
- # empty response wrap it in empty object
- return new JsonObject
- else if res isa JsonObject and res.has_key("exception") then
- var error = "Neo4jError::{res["exception"] or else "null"}"
- var msg = ""
- if res.has_key("message") then
- msg = res["message"].to_s
- end
- return new JsonError(error, msg.to_json)
- else
- return res
+ else if res isa JsonObject and res.has_key("exception") then
+ var error = "Neo4jError::{res["exception"] or else "null"}"
+ var msg = ""
+ if res.has_key("message") then
+ msg = res["message"].to_s
end
+ return new NeoError(msg, error)
+ else
+ return res
end
else if response isa CurlResponseFailed then
- return new JsonError("Curl error", "{response.error_msg} ({response.error_code})")
+ return new NeoError("{response.error_msg} ({response.error_code})", "CurlError")
else
- return new JsonError("Curl error", "Unexpected response '{response}'")
+ return new NeoError("Unexpected response \"{response}\".", "CurlError")
end
end
end
fun save_edges(edges: Collection[NeoEdge]) do for edge in edges do save_edge(edge)
# Execute the batch and update local nodes
- fun execute: List[JsonError] do
+ fun execute: List[NeoError] do
var request = new JsonPOST(client.batch_url, client.curl)
# request.headers["X-Stream"] = "true"
var json_jobs = new JsonArray
end
# Associate data from response in original nodes and edges
- private fun finalize_batch(response: Jsonable): List[JsonError] do
- var errors = new List[JsonError]
+ private fun finalize_batch(response: Jsonable): List[NeoError] do
+ var errors = new List[NeoError]
if not response isa JsonArray then
- errors.add(new JsonError("Neo4jError", "Unexpected batch response format"))
+ errors.add(new NeoError("Unexpected batch response format.", "Neo4jError"))
return errors
end
# print " {res.length} jobs executed"
for res in response do
if not res isa JsonObject then
- errors.add(new JsonError("Neo4jError", "Unexpected job format in batch response"))
+ errors.add(new NeoError("Unexpected job format in batch response.", "Neo4jError"))
continue
end
var id = res["id"].as(Int)
import toolcontext
import doc_model
+private import json::static
redef class ToolContext
private var opt_dir = new OptionString("output directory", "-d", "--dir")
var tpl = new Template
tpl.add "var nitdocQuickSearchRawList=\{ "
for mmodule in mmodules do
- tpl.add "\"{mmodule.name}\":["
- tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\},"
+ tpl.add "{mmodule.name.to_json}:["
+ tpl.add "\{txt:{mmodule.full_name.to_json},url:{mmodule.nitdoc_url.to_json}\},"
tpl.add "],"
end
for mclass in mclasses do
var full_name = mclass.intro.mmodule.full_name
- tpl.add "\"{mclass.name}\":["
- tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\},"
+ tpl.add "{mclass.name.to_json}:["
+ tpl.add "\{txt:{full_name.to_json},url:{mclass.nitdoc_url.to_json}\},"
tpl.add "],"
end
for mproperty, mprops in mpropdefs do
- tpl.add "\"{mproperty}\":["
+ tpl.add "{mproperty.to_json}:["
for mpropdef in mprops do
var full_name = mpropdef.mclassdef.mclass.full_name
var cls_url = mpropdef.mclassdef.mclass.nitdoc_url
var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}"
- tpl.add "\{txt:\"{full_name}\",url:\"{def_url}\"\},"
+ tpl.add "\{txt:{full_name.to_json},url:{def_url.to_json}\},"
end
tpl.add "],"
end
if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self)
if name == "Array[String]" then return new Array[String].from_deserializer(self)
if name == "Array[Object]" then return new Array[Object].from_deserializer(self)
- if name == "Array[Match]" then return new Array[Match].from_deserializer(self)
- if name == "Array[FlatBuffer]" then return new Array[FlatBuffer].from_deserializer(self)
return super
end
end
# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
var a = "\{\"__kind\": \"obj\", \"__id\": 0, \"__class\": \"C\", \"a\": \{\"__kind\": \"obj\", \"__id\": 1, \"__class\": \"A\", \"b\": true, \"c\": \"a\", \"f\": 0.123, \"i\": 1234, \"s\": \"asdf\", \"n\": null, \"array\": [88, \"hello\", null]\}, \"b\": \{\"__kind\": \"obj\", \"__id\": 2, \"__class\": \"B\", \"b\": false, \"c\": \"b\", \"f\": 123.123, \"i\": 2345, \"s\": \"hjkl\", \"n\": null, \"array\": [88, \"hello\", null], \"ii\": 1111, \"ss\": \"qwer\"\}, \"aa\": \{\"__kind\": \"ref\", \"__id\": 1\}\}"
var b = "\{\"__kind\": \"obj\", \"__id\": 0, \"__class\": \"A\", \"b\": true, \"c\": \"a\", \"f\": 0.123, \"i\": 1234, \"s\": \"asdf\", \"n\": null, \"array\": [88, \"hello\", null]\}"
+var c = "\{\"foo\":\"bar\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0020\\u0000\"\}"
+var d = "\{ \"face with tears of joy\" : \"\\uD83D\\uDE02\" \}"
-for s in [a, b] do
- var obj = s.json_to_nit_object
+for s in [a, b, c, d] do
+ var obj = s.parse_json
print "# Json: {s}"
print "# Nit: {obj or else "<null>"}"
end
print res1["name"].to_s
print res1["age"].to_s
print res1["status"].to_s
-print res1["groups"].to_s
+print res1["groups"].to_json
print res1.labels.join(" ")
assert res1.out_edges.is_empty
print res4["name"].to_s
print res4["age"].to_s
print res4["status"].to_s
-print res4["groups"].to_s
+print res4["groups"].to_json
print res4.labels.join(" ")
assert res4.in_edges.is_empty
assert not res4.out_edges.is_empty
print res4["name"].to_s
print res4["age"].to_s
print res4["status"].to_s
-print res4["groups"].to_s
+print res4["groups"].to_json
print res4.labels.join(" ")
assert res4.in_edges.is_empty
assert not res4.out_edges.is_empty