1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 # Copyright 2014 Alexandre Terrasa <alexandre@moz-concept.com>
5 # Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
19 # Static interface to read Nit objects from JSON strings
21 # `Text::parse_json` returns a simple Nit object from the JSON source.
22 # This object can then be type checked as usual with `isa` and `as`.
26 private import json_parser
27 private import json_lexer
29 # Something that can be translated to JSON.
37 # Removes JSON-escaping if necessary in a JSON string
39 # assert "\\\"string\\uD83D\\uDE02\\\"".unescape_json == "\"string😂\""
40 fun unescape_json
: Text do
41 if not json_need_escape
then return self
42 return self.json_to_nit_string
45 # Does `self` need treatment from JSON to Nit ?
47 # i.e. is there at least one `\` character in it ?
49 # assert not "string".json_need_escape
50 # assert "\\\"string\\\"".json_need_escape
51 protected fun json_need_escape
: Bool do return has
('\\')
53 # Escapes `self` from a JSON string to a Nit string
55 # assert "\\\"string\\\"".json_to_nit_string == "\"string\""
56 # assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
57 # assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
58 protected fun json_to_nit_string
: String do
59 var res
= new FlatBuffer.with_capacity
(byte_length
)
68 char
= 0x08.code_point
69 else if char
== 'f' then
70 char
= 0x0C.code_point
71 else if char
== 'n' then
73 else if char
== 'r' then
75 else if char
== 't' then
77 else if char
== 'u' then
78 var u16_esc
= from_utf16_digit
(i
+ 1)
79 char
= u16_esc
.code_point
80 if char
.is_surrogate
and i
+ 10 < ln
then
81 if self[i
+ 5] == '\\' and self[i
+ 6] == 'u' then
83 u16_esc
+= from_utf16_digit
(i
+ 7)
84 char
= u16_esc
.to_u32
.from_utf16_surr
.code_point
87 char
= 0xFFFD.code_point
92 # `"`, `/` or `\` => Keep `char` as-is.
100 # Parse `self` as JSON.
102 # If `self` is not a valid JSON document or contains an unsupported escape
103 # sequence, return a `JSONParseError`.
105 # Example with `JsonObject`:
107 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".parse_json
108 # assert obj isa JsonObject
109 # assert obj["foo"] isa JsonObject
110 # assert obj["foo"].as(JsonObject)["bar"] == true
112 # Example with `JsonArray`:
114 # var arr = "[1, 2, 3]".parse_json
115 # assert arr isa JsonArray
116 # assert arr.length == 3
117 # assert arr.first == 1
118 # assert arr.last == 3
120 # Example with `String`:
122 # var str = "\"foo, bar, baz\"".parse_json
123 # assert str isa String
124 # assert str == "foo, bar, baz"
126 # Example of a syntaxic error:
128 # var bad = "\{foo: \"bar\"\}".parse_json
129 # assert bad isa JsonParseError
130 # assert bad.position.col_start == 2
131 fun parse_json
: nullable Jsonable do
132 var lexer
= new Lexer_json(to_s
)
133 var parser
= new Parser_json
134 var tokens
= lexer
.lex
135 parser
.tokens
.add_all
(tokens
)
136 var root_node
= parser
.parse
137 if root_node
isa NStart then
138 return root_node
.n_0
.to_nit_object
139 else if root_node
isa NError then
140 return new JsonParseError(root_node
.message
, root_node
.position
)
146 redef fun json_need_escape
do
148 for i
in [first_byte
.. last_byte
] do
149 if its
[i
] == 0x5Cu
8 then return true
167 # A map that can be translated into a JSON object.
168 interface JsonMapRead[K
: String, V
: nullable Jsonable]
175 super JsonMapRead[String, nullable Jsonable]
176 super HashMap[String, nullable Jsonable]
179 # A sequence that can be translated into a JSON array.
180 class JsonSequenceRead[E
: nullable Jsonable]
182 super SequenceRead[E
]
187 super JsonSequenceRead[nullable Jsonable]
188 super Array[nullable Jsonable]
191 redef class JsonParseError
199 ################################################################################
203 # The represented value.
204 private fun to_nit_object
: nullable Jsonable is abstract
207 redef class Nvalue_number
208 redef fun to_nit_object
210 var text
= n_number
.text
211 if text
.chars
.has
('.') or text
.chars
.has
('e') or text
.chars
.has
('E') then return text
.to_f
216 redef class Nvalue_string
217 redef fun to_nit_object
do return n_string
.to_nit_string
220 redef class Nvalue_true
221 redef fun to_nit_object
do return true
224 redef class Nvalue_false
225 redef fun to_nit_object
do return false
228 redef class Nvalue_null
229 redef fun to_nit_object
do return null
233 # The represented string.
234 private fun to_nit_string
: String do return text
.substring
(1, text
.length
- 2).unescape_json
.to_s
237 redef class Nvalue_object
238 redef fun to_nit_object
do
239 var obj
= new JsonObject
240 var members
= n_members
241 if members
!= null then
242 var pairs
= members
.pairs
243 for pair
in pairs
do obj
[pair
.name
] = pair
.value
250 # All the key-value pairs.
251 private fun pairs
: Array[Npair] is abstract
254 redef class Nmembers_tail
257 var arr
= n_members
.pairs
263 redef class Nmembers_head
264 redef fun pairs
do return [n_pair
]
268 # The represented key.
269 private fun name
: String do return n_string
.to_nit_string
271 # The represented value.
272 private fun value
: nullable Jsonable do return n_value
.to_nit_object
275 redef class Nvalue_array
276 redef fun to_nit_object
278 var arr
= new JsonArray
279 var elements
= n_elements
280 if elements
!= null then
281 var items
= elements
.items
282 for item
in items
do arr
.add
(item
.to_nit_object
)
288 redef class Nelements
290 private fun items
: Array[Nvalue] is abstract
293 redef class Nelements_tail
296 var items
= n_elements
.items
302 redef class Nelements_head
303 redef fun items
do return [n_value
]