1 # You may obtain a copy of the License at
3 # http://www.apache.org/licenses/LICENSE-2.0
5 # Unless required by applicable law or agreed to in writing, software
6 # distributed under the License is distributed on an "AS IS" BASIS,
7 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8 # See the License for the specific language governing permissions and
9 # limitations under the License.
11 # Introduce base classes and services for JSON handling.
15 private import json
::json_parser
16 private import json
::json_lexer
18 # Something that can be translated to JSON
20 # Get the JSON representation of `self`
21 fun to_json
: String is abstract
28 var res
= new FlatBuffer
30 for i
in [0..self.length
[ do
35 else if char
== '\"' then
38 else if char == '\/' then
41 else if char == '\n' then
44 else if char == '\r' then
47 else if char == '\t' then
54 return res.write_to_string
61 redef fun to_json do return self.to_s
67 redef fun to_json do return self.to_s
73 redef fun to_json do return self.to_s
76 # A JSON Object representation that behaves like a `Map`
79 super Map[String, nullable Jsonable]
81 private var map = new HashMap[String, nullable Jsonable]
83 # Create an empty `JsonObject`
85 # var obj = new JsonObject
89 # Init the JSON Object from a Nit `Map`
91 # var map = new HashMap[String, String]
94 # var obj = new JsonObject.from(map)
95 # assert obj.length == 2
96 # assert obj["foo"] == "bar"
97 # assert obj["goo"] == "baz"
98 init from(items: Map[String, nullable Jsonable]) do
99 for k, v in items do map[k] = v
102 redef fun [](key) do return map[key]
103 redef fun []=(key, value) do map[key] = value
104 redef fun clear do map.clear
105 redef fun has_key(key) do return map.has_key(key)
106 redef fun is_empty do return map.is_empty
107 redef fun iterator do return map.iterator
108 redef fun keys do return map.keys
109 redef fun values do return map.values
110 redef fun length do return map.length
112 # Advanced query to get a value within `self` or its children.
114 # A query is composed of the keys to each object seperated by '.'.
116 # REQUIRE `self.has_key(query)`
118 # var obj1 = new JsonObject
119 # obj1["baz"] = "foobarbaz"
120 # var obj2 = new JsonObject
122 # var obj3 = new JsonObject
124 # assert obj3.get("foo.bar.baz") == "foobarbaz"
125 fun get(query: String): nullable Jsonable do
126 var keys = query.split(".").reversed
132 while not keys.is_empty do
134 assert node isa JsonObject and node.has_key(key)
140 # Create an empty `JsonObject`
142 # var obj = new JsonObject
144 # assert obj.to_json == "\{\"foo\": \"bar\"\}"
146 var tpl = new Array[String]
148 var vals = new Array[String]
151 vals.add "{k.to_json}: null"
153 vals.add "{k.to_json}: {v.to_json}"
156 tpl.add vals.join(",")
161 redef fun to_s do return to_json
164 # A JSON Array representation that behaves like a `Sequence`
167 super Sequence[nullable Jsonable]
169 private var array = new Array[nullable Jsonable]
173 # init the JSON Array from a Nit `Collection`
174 init from(items: Collection[nullable Jsonable]) do
178 redef fun [](key) do return array[key]
179 redef fun []=(key, value) do array[key] = value
180 redef fun add(value) do array.add(value)
181 redef fun clear do array.clear
182 redef fun is_empty do return array.is_empty
183 redef fun iterator do return array.iterator
184 redef fun length do return array.length
187 var tpl = new Array[String]
189 var vals = new Array[String]
197 tpl.add vals.join(",")
202 redef fun to_s do return to_json
205 # An error in JSON format that can be returned by tools using JSON like parsers.
207 # var error = new JsonError("ErrorCode", "ErrorMessage")
208 # assert error.to_s == "ErrorCode: ErrorMessage"
209 # assert error.to_json == "\{\"error\": \"ErrorCode\", \"message\": \"ErrorMessage\"\}"
220 var tpl = new Array[String]
222 tpl.add "\"error\": {error.to_json}, "
223 tpl.add "\"message\": {message.to_json}"
228 redef fun to_s do return "{error}: {message}"
234 private fun to_nit_object: nullable Jsonable is abstract
237 redef class Nvalue_number
238 redef fun to_nit_object
240 var text = n_number.text
241 if text.chars.has('.') or text.chars.has('e
') or text.chars.has('E
') then return text.to_f
246 redef class Nvalue_string
247 redef fun to_nit_object do return n_string.to_nit_string
250 redef class Nvalue_true
251 redef fun to_nit_object do return true
254 redef class Nvalue_false
255 redef fun to_nit_object do return false
258 redef class Nvalue_null
259 redef fun to_nit_object do return null
263 # FIXME support \n, etc.
264 fun to_nit_string: String do
265 var res = new FlatBuffer
267 for i in [1..text.length-2] do
273 if char == '\\
' and i < text.length - 2 then
274 if text[i + 1] == '\\
' then
279 if text[i + 1] == '\
"' then
284 if text[i + 1] == '/' then
289 if text[i + 1] == 'n
' then
294 if text[i + 1] == 'r
' then
299 if text[i + 1] == 't
' then
307 return res.write_to_string
311 redef class Nvalue_object
312 redef fun to_nit_object
314 var obj = new JsonObject
315 var members = n_members
316 if members != null then
317 var pairs = members.pairs
318 for pair in pairs do obj[pair.name] = pair.value
325 fun pairs: Array[Npair] is abstract
328 redef class Nmembers_tail
331 var arr = n_members.pairs
337 redef class Nmembers_head
338 redef fun pairs do return [n_pair]
342 fun name: String do return n_string.to_nit_string
343 fun value: nullable Jsonable do return n_value.to_nit_object
346 redef class Nvalue_array
347 redef fun to_nit_object
349 var arr = new JsonArray
350 var elements = n_elements
351 if elements != null then
352 var items = elements.items
353 for item in items do arr.add(item.to_nit_object)
359 redef class Nelements
360 fun items: Array[Nvalue] is abstract
363 redef class Nelements_tail
366 var items = n_elements.items
372 redef class Nelements_head
373 redef fun items do return [n_value]
377 # Parse a JSON String as Jsonable entities
379 # Example with `JsonObject`"
381 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".to_jsonable
382 # assert obj isa JsonObject
383 # assert obj["foo"] isa JsonObject
384 # assert obj["foo"].as(JsonObject)["bar"] == true
386 # Example with `JsonArray`
388 # var arr = "[1, 2, 3]".to_jsonable
389 # assert arr isa JsonArray
390 # assert arr.length == 3
391 # assert arr.first == 1
392 # assert arr.last == 3
394 # Example with `String`
396 # var str = "\"foo, bar, baz\"".to_jsonable
397 # assert str isa String
398 # assert str == "foo, bar, baz"
400 # Malformed JSON input returns a `JsonError` object
402 # var bad = "\{foo: \"bar\"\}".to_jsonable
403 # assert bad isa JsonError
404 # assert bad.error == "JsonLexerError"
405 fun to_jsonable: nullable Jsonable
407 var lexer = new Lexer_json(to_s)
408 var parser = new Parser_json
409 var tokens = lexer.lex
410 parser.tokens.add_all(tokens)
411 var root_node = parser.parse
412 if root_node isa NStart then
413 return root_node.n_0.to_nit_object
414 else if root_node isa NLexerError then
415 var pos = root_node.position
416 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
417 return new JsonError("JsonLexerError", msg)
418 else if root_node isa NParserError then
419 var pos = root_node.position
420 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
421 return new JsonError("JsonParsingError", msg)