1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Introduce base classes and services for JSON handling.
19 private import json
::json_parser
20 private import json
::json_lexer
22 # Something that can be translated to JSON
24 # Get the JSON representation of `self`
25 fun to_json
: String is abstract
32 var res
= new FlatBuffer
34 for i
in [0..self.length
[ do
39 else if char
== '\"' then
42 else if char == '\/' then
45 else if char == '\n' then
48 else if char == '\r' then
51 else if char == '\t' then
58 return res.write_to_string
65 redef fun to_json do return self.to_s
71 redef fun to_json do return self.to_s
77 redef fun to_json do return self.to_s
80 # A JSON Object representation that behaves like a `Map`
83 super Map[String, nullable Jsonable]
85 private var map = new HashMap[String, nullable Jsonable]
87 # Create an empty `JsonObject`
89 # var obj = new JsonObject
93 # Init the JSON Object from a Nit `Map`
95 # var map = new HashMap[String, String]
98 # var obj = new JsonObject.from(map)
99 # assert obj.length == 2
100 # assert obj["foo"] == "bar"
101 # assert obj["goo"] == "baz"
102 init from(items: Map[String, nullable Jsonable]) do
103 for k, v in items do map[k] = v
106 redef fun [](key) do return map[key]
107 redef fun []=(key, value) do map[key] = value
108 redef fun clear do map.clear
109 redef fun has_key(key) do return map.has_key(key)
110 redef fun is_empty do return map.is_empty
111 redef fun iterator do return map.iterator
112 redef fun keys do return map.keys
113 redef fun values do return map.values
114 redef fun length do return map.length
116 # Advanced query to get a value within `self` or its children.
118 # A query is composed of the keys to each object seperated by '.'.
120 # REQUIRE `self.has_key(query)`
122 # var obj1 = new JsonObject
123 # obj1["baz"] = "foobarbaz"
124 # var obj2 = new JsonObject
126 # var obj3 = new JsonObject
128 # assert obj3.get("foo.bar.baz") == "foobarbaz"
129 fun get(query: String): nullable Jsonable do
130 var keys = query.split(".").reversed
136 while not keys.is_empty do
138 assert node isa JsonObject and node.has_key(key)
144 # Create an empty `JsonObject`
146 # var obj = new JsonObject
148 # assert obj.to_json == "\{\"foo\": \"bar\"\}"
150 var tpl = new Array[String]
152 var vals = new Array[String]
155 vals.add "{k.to_json}: null"
157 vals.add "{k.to_json}: {v.to_json}"
160 tpl.add vals.join(",")
165 redef fun to_s do return to_json
168 # A JSON Array representation that behaves like a `Sequence`
171 super Sequence[nullable Jsonable]
173 private var array = new Array[nullable Jsonable]
177 # init the JSON Array from a Nit `Collection`
178 init from(items: Collection[nullable Jsonable]) do
182 redef fun [](key) do return array[key]
183 redef fun []=(key, value) do array[key] = value
184 redef fun clear do array.clear
185 redef fun insert(item, index) do array.insert(item, index)
186 redef fun is_empty do return array.is_empty
187 redef fun iterator do return array.iterator
188 redef fun length do return array.length
189 redef fun pop do return array.pop
190 redef fun push(value) do array.push(value)
191 redef fun remove_at(index) do array.remove_at(index)
192 redef fun shift do return array.shift
193 redef fun unshift(e) do array.unshift(e)
196 var tpl = new Array[String]
198 var vals = new Array[String]
206 tpl.add vals.join(",")
211 redef fun to_s do return to_json
214 # An error in JSON format that can be returned by tools using JSON like parsers.
216 # var error = new JsonError("ErrorCode", "ErrorMessage")
217 # assert error.to_s == "ErrorCode: ErrorMessage"
218 # assert error.to_json == "\{\"error\": \"ErrorCode\", \"message\": \"ErrorMessage\"\}"
229 var tpl = new Array[String]
231 tpl.add "\"error\": {error.to_json}, "
232 tpl.add "\"message\": {message.to_json}"
237 redef fun to_s do return "{error}: {message}"
243 private fun to_nit_object: nullable Jsonable is abstract
246 redef class Nvalue_number
247 redef fun to_nit_object
249 var text = n_number.text
250 if text.chars.has('.') or text.chars.has('e
') or text.chars.has('E
') then return text.to_f
255 redef class Nvalue_string
256 redef fun to_nit_object do return n_string.to_nit_string
259 redef class Nvalue_true
260 redef fun to_nit_object do return true
263 redef class Nvalue_false
264 redef fun to_nit_object do return false
267 redef class Nvalue_null
268 redef fun to_nit_object do return null
272 # FIXME support \n, etc.
273 fun to_nit_string: String do
274 var res = new FlatBuffer
276 for i in [1..text.length-2] do
282 if char == '\\
' and i < text.length - 2 then
283 if text[i + 1] == '\\
' then
288 if text[i + 1] == '\
"' then
293 if text[i + 1] == '/' then
298 if text[i + 1] == 'n
' then
303 if text[i + 1] == 'r
' then
308 if text[i + 1] == 't
' then
316 return res.write_to_string
320 redef class Nvalue_object
321 redef fun to_nit_object
323 var obj = new JsonObject
324 var members = n_members
325 if members != null then
326 var pairs = members.pairs
327 for pair in pairs do obj[pair.name] = pair.value
334 fun pairs: Array[Npair] is abstract
337 redef class Nmembers_tail
340 var arr = n_members.pairs
346 redef class Nmembers_head
347 redef fun pairs do return [n_pair]
351 fun name: String do return n_string.to_nit_string
352 fun value: nullable Jsonable do return n_value.to_nit_object
355 redef class Nvalue_array
356 redef fun to_nit_object
358 var arr = new JsonArray
359 var elements = n_elements
360 if elements != null then
361 var items = elements.items
362 for item in items do arr.add(item.to_nit_object)
368 redef class Nelements
369 fun items: Array[Nvalue] is abstract
372 redef class Nelements_tail
375 var items = n_elements.items
381 redef class Nelements_head
382 redef fun items do return [n_value]
386 # Parse a JSON String as Jsonable entities
388 # Example with `JsonObject`"
390 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".to_jsonable
391 # assert obj isa JsonObject
392 # assert obj["foo"] isa JsonObject
393 # assert obj["foo"].as(JsonObject)["bar"] == true
395 # Example with `JsonArray`
397 # var arr = "[1, 2, 3]".to_jsonable
398 # assert arr isa JsonArray
399 # assert arr.length == 3
400 # assert arr.first == 1
401 # assert arr.last == 3
403 # Example with `String`
405 # var str = "\"foo, bar, baz\"".to_jsonable
406 # assert str isa String
407 # assert str == "foo, bar, baz"
409 # Malformed JSON input returns a `JsonError` object
411 # var bad = "\{foo: \"bar\"\}".to_jsonable
412 # assert bad isa JsonError
413 # assert bad.error == "JsonLexerError"
414 fun to_jsonable: nullable Jsonable
416 var lexer = new Lexer_json(to_s)
417 var parser = new Parser_json
418 var tokens = lexer.lex
419 parser.tokens.add_all(tokens)
420 var root_node = parser.parse
421 if root_node isa NStart then
422 return root_node.n_0.to_nit_object
423 else if root_node isa NLexerError then
424 var pos = root_node.position
425 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
426 return new JsonError("JsonLexerError", msg)
427 else if root_node isa NParserError then
428 var pos = root_node.position
429 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
430 return new JsonError("JsonParsingError", msg)