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
31 # Removes JSON-escaping if necessary in a JSON string
33 # assert "\\\"string\\uD83D\\uDE02\\\"".unescape_json == "\"string😂\""
34 fun unescape_json
: Text do
35 if not json_need_escape
then return self
36 return self.json_to_nit_string
39 # Does `self` need treatment from JSON to Nit ?
41 # i.e. is there at least one `\` character in it ?
43 # assert not "string".json_need_escape
44 # assert "\\\"string\\\"".json_need_escape
45 protected fun json_need_escape
: Bool do return has
('\\')
47 # Escapes `self` from a JSON string to a Nit string
49 # assert "\\\"string\\\"".json_to_nit_string == "\"string\""
50 # assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n"
51 # assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓"
52 protected fun json_to_nit_string
: String do
53 var res
= new FlatBuffer.with_capacity
(byte_length
)
62 char
= 0x08.code_point
63 else if char
== 'f' then
64 char
= 0x0C.code_point
65 else if char
== 'n' then
67 else if char
== 'r' then
69 else if char
== 't' then
71 else if char
== 'u' then
72 var u16_esc
= from_utf16_digit
(i
+ 1)
73 char
= u16_esc
.code_point
74 if char
.is_surrogate
and i
+ 10 < ln
then
75 if self[i
+ 5] == '\\' and self[i
+ 6] == 'u' then
77 u16_esc
+= from_utf16_digit
(i
+ 7)
78 char
= u16_esc
.to_u32
.from_utf16_surr
.code_point
81 char
= 0xFFFD.code_point
86 # `"`, `/` or `\` => Keep `char` as-is.
94 # Parse `self` as JSON.
96 # If `self` is not a valid JSON document or contains an unsupported escape
97 # sequence, return a `JSONParseError`.
99 # Example with `JsonObject`:
101 # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".parse_json
102 # assert obj isa JsonObject
103 # assert obj["foo"] isa JsonObject
104 # assert obj["foo"].as(JsonObject)["bar"] == true
106 # Example with `JsonArray`:
108 # var arr = "[1, 2, 3]".parse_json
109 # assert arr isa JsonArray
110 # assert arr.length == 3
111 # assert arr.first == 1
112 # assert arr.last == 3
114 # Example with `String`:
116 # var str = "\"foo, bar, baz\"".parse_json
117 # assert str isa String
118 # assert str == "foo, bar, baz"
120 # Example of a syntaxic error:
122 # var bad = "\{foo: \"bar\"\}".parse_json
123 # assert bad isa JsonParseError
124 # assert bad.position.col_start == 2
125 fun parse_json
: nullable Serializable do
126 var lexer
= new Lexer_json(to_s
)
127 var parser
= new Parser_json
128 var tokens
= lexer
.lex
129 parser
.tokens
.add_all
(tokens
)
130 var root_node
= parser
.parse
131 if root_node
isa NStart then
132 return root_node
.n_0
.to_nit_object
133 else if root_node
isa NError then
134 return new JsonParseError(root_node
.message
, root_node
.position
)
140 redef fun json_need_escape
do
142 for i
in [first_byte
.. last_byte
] do
143 if its
[i
] == 0x5Cu
8 then return true
149 # A map that can be translated into a JSON object.
150 interface JsonMapRead[K
: String, V
: nullable Serializable]
157 super JsonMapRead[String, nullable Serializable]
158 super HashMap[String, nullable Serializable]
161 # A sequence that can be translated into a JSON array.
162 class JsonSequenceRead[E
: nullable Serializable]
164 super SequenceRead[E
]
169 super JsonSequenceRead[nullable Serializable]
170 super Array[nullable Serializable]
173 ################################################################################
177 # The represented value.
178 private fun to_nit_object
: nullable Serializable is abstract
181 redef class Nvalue_number
182 redef fun to_nit_object
184 var text
= n_number
.text
185 if text
.chars
.has
('.') or text
.chars
.has
('e') or text
.chars
.has
('E') then return text
.to_f
190 redef class Nvalue_string
191 redef fun to_nit_object
do return n_string
.to_nit_string
194 redef class Nvalue_true
195 redef fun to_nit_object
do return true
198 redef class Nvalue_false
199 redef fun to_nit_object
do return false
202 redef class Nvalue_null
203 redef fun to_nit_object
do return null
207 # The represented string.
208 private fun to_nit_string
: String do return text
.substring
(1, text
.length
- 2).unescape_json
.to_s
211 redef class Nvalue_object
212 redef fun to_nit_object
do
213 var obj
= new JsonObject
214 var members
= n_members
215 if members
!= null then
216 var pairs
= members
.pairs
217 for pair
in pairs
do obj
[pair
.name
] = pair
.value
224 # All the key-value pairs.
225 private fun pairs
: Array[Npair] is abstract
228 redef class Nmembers_tail
231 var arr
= n_members
.pairs
237 redef class Nmembers_head
238 redef fun pairs
do return [n_pair
]
242 # The represented key.
243 private fun name
: String do return n_string
.to_nit_string
245 # The represented value.
246 private fun value
: nullable Serializable do return n_value
.to_nit_object
249 redef class Nvalue_array
250 redef fun to_nit_object
252 var arr
= new JsonArray
253 var elements
= n_elements
254 if elements
!= null then
255 var items
= elements
.items
256 for item
in items
do arr
.add
(item
.to_nit_object
)
262 redef class Nelements
264 private fun items
: Array[Nvalue] is abstract
267 redef class Nelements_tail
270 var items
= n_elements
.items
276 redef class Nelements_head
277 redef fun items
do return [n_value
]