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]
175 # init the JSON Array from a Nit `Collection`
176 init from(items: Collection[nullable Jsonable]) do
180 redef fun [](key) do return array[key]
181 redef fun []=(key, value) do array[key] = value
182 redef fun clear do array.clear
183 redef fun insert(item, index) do array.insert(item, index)
184 redef fun is_empty do return array.is_empty
185 redef fun iterator do return array.iterator
186 redef fun length do return array.length
187 redef fun pop do return array.pop
188 redef fun push(value) do array.push(value)
189 redef fun remove_at(index) do array.remove_at(index)
190 redef fun shift do return array.shift
191 redef fun unshift(e) do array.unshift(e)
194 var tpl = new Array[String]
196 var vals = new Array[String]
204 tpl.add vals.join(",")
209 redef fun to_s do return to_json
212 # An error in JSON format that can be returned by tools using JSON like parsers.
214 # var error = new JsonError("ErrorCode", "ErrorMessage")
215 # assert error.to_s == "ErrorCode: ErrorMessage"
216 # assert error.to_json == "\{\"error\": \"ErrorCode\", \"message\": \"ErrorMessage\"\}"
227 var tpl = new Array[String]
229 tpl.add "\"error\": {error.to_json}, "
230 tpl.add "\"message\": {message.to_json}"
235 redef fun to_s do return "{error}: {message}"
241 private fun to_nit_object: nullable Jsonable is abstract
244 redef class Nvalue_number
245 redef fun to_nit_object
247 var text = n_number.text
248 if text.chars.has('.') or text.chars.has('e
') or text.chars.has('E
') then return text.to_f
253 redef class Nvalue_string
254 redef fun to_nit_object do return n_string.to_nit_string
257 redef class Nvalue_true
258 redef fun to_nit_object do return true
261 redef class Nvalue_false
262 redef fun to_nit_object do return false
265 redef class Nvalue_null
266 redef fun to_nit_object do return null
270 # FIXME support \n, etc.
271 fun to_nit_string: String do
272 var res = new FlatBuffer
274 for i in [1..text.length-2] do
280 if char == '\\
' and i < text.length - 2 then
281 if text[i + 1] == '\\
' then
286 if text[i + 1] == '\
"' then
291 if text[i + 1] == '/' then
296 if text[i + 1] == 'n
' then
301 if text[i + 1] == 'r
' then
306 if text[i + 1] == 't
' then
314 return res.write_to_string
318 redef class Nvalue_object
319 redef fun to_nit_object
321 var obj = new JsonObject
322 var members = n_members
323 if members != null then
324 var pairs = members.pairs
325 for pair in pairs do obj[pair.name] = pair.value
332 fun pairs: Array[Npair] is abstract
335 redef class Nmembers_tail
338 var arr = n_members.pairs
344 redef class Nmembers_head
345 redef fun pairs do return [n_pair]
349 fun name: String do return n_string.to_nit_string
350 fun value: nullable Jsonable do return n_value.to_nit_object
353 redef class Nvalue_array
354 redef fun to_nit_object
356 var arr = new JsonArray
357 var elements = n_elements
358 if elements != null then
359 var items = elements.items
360 for item in items do arr.add(item.to_nit_object)
366 redef class Nelements
367 fun items: Array[Nvalue] is abstract
370 redef class Nelements_tail
373 var items = n_elements.items
379 redef class Nelements_head
380 redef fun items do return [n_value]
384 # Parse a JSON String as Jsonable entities
386 # Example with `JsonObject`"
388 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".to_jsonable
389 # assert obj isa JsonObject
390 # assert obj["foo"] isa JsonObject
391 # assert obj["foo"].as(JsonObject)["bar"] == true
393 # Example with `JsonArray`
395 # var arr = "[1, 2, 3]".to_jsonable
396 # assert arr isa JsonArray
397 # assert arr.length == 3
398 # assert arr.first == 1
399 # assert arr.last == 3
401 # Example with `String`
403 # var str = "\"foo, bar, baz\"".to_jsonable
404 # assert str isa String
405 # assert str == "foo, bar, baz"
407 # Malformed JSON input returns a `JsonError` object
409 # var bad = "\{foo: \"bar\"\}".to_jsonable
410 # assert bad isa JsonError
411 # assert bad.error == "JsonLexerError"
412 fun to_jsonable: nullable Jsonable
414 var lexer = new Lexer_json(to_s)
415 var parser = new Parser_json
416 var tokens = lexer.lex
417 parser.tokens.add_all(tokens)
418 var root_node = parser.parse
419 if root_node isa NStart then
420 return root_node.n_0.to_nit_object
421 else if root_node isa NLexerError then
422 var pos = root_node.position
423 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
424 return new JsonError("JsonLexerError", msg)
425 else if root_node isa NParserError then
426 var pos = root_node.position
427 var msg = "{root_node.message} at {pos or else "<unknown>"} for {root_node}"
428 return new JsonError("JsonParsingError", msg)