Merge: share/libgc: option to use a local version of the source pkgs
[nit.git] / lib / json_serialization.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Handles serialization and deserialization of objects to/from Json.
18 module json_serialization
19
20 import serialization
21 import json::static
22
23 # Serializer of Nit objects to Json string.
24 class JsonSerializer
25 super Serializer
26
27 # Target writing stream
28 var stream: Writer
29
30 redef fun serialize(object)
31 do
32 if object == null then
33 stream.write "null"
34 else object.serialize_to_json(self)
35 end
36
37 redef fun serialize_attribute(name, value)
38 do
39 stream.write ", \"{name}\": "
40 super
41 end
42
43 redef fun serialize_reference(object)
44 do
45 if refs_map.has_key(object) then
46 # if already serialized, add local reference
47 var id = ref_id_for(object)
48 stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}"
49 else
50 # serialize here
51 serialize object
52 end
53 end
54
55 # Map of references to already serialized objects.
56 private var refs_map = new StrictHashMap[Serializable,Int]
57
58 # Get the internal serialized reference for this `object`.
59 private fun ref_id_for(object: Serializable): Int
60 do
61 if refs_map.has_key(object) then
62 return refs_map[object]
63 else
64 var id = refs_map.length
65 refs_map[object] = id
66 return id
67 end
68 end
69 end
70
71 # Deserializer from a Json string.
72 class JsonDeserializer
73 super Deserializer
74
75 # Json text to deserialize from.
76 private var text: Text
77
78 # Root json object parsed from input text.
79 var root: nullable Jsonable is noinit
80
81 # Depth-first path in the serialized object tree.
82 var path = new Array[JsonObject]
83
84 # Map of references to already deserialized objects.
85 private var id_to_object = new StrictHashMap[Int, Object]
86
87 # Last encountered object reference id.
88 #
89 # See `id_to_object`.
90 var just_opened_id: nullable Int = null
91
92 init do
93 var root = text.parse_json
94 if root isa JsonObject then path.add(root)
95 self.root = root
96 end
97
98 redef fun deserialize_attribute(name)
99 do
100 assert not path.is_empty
101 var current = path.last
102
103 assert current.keys.has(name)
104 var value = current[name]
105
106 return convert_object(value)
107 end
108
109 # This may be called multiple times by the same object from constructors
110 # in different nclassdef
111 redef fun notify_of_creation(new_object)
112 do
113 var id = just_opened_id
114 if id == null then return # Register `new_object` only once
115 id_to_object[id] = new_object
116 end
117
118 # Convert from simple Json object to Nit object
119 private fun convert_object(object: nullable Object): nullable Object
120 do
121 if object isa JsonObject then
122 assert object.keys.has("__kind")
123 var kind = object["__kind"]
124
125 # ref?
126 if kind == "ref" then
127 assert object.keys.has("__id")
128 var id = object["__id"]
129 assert id isa Int
130
131 assert id_to_object.has_key(id)
132 return id_to_object[id]
133 end
134
135 # obj?
136 if kind == "obj" then
137 assert object.keys.has("__id")
138 var id = object["__id"]
139 assert id isa Int
140
141 assert object.keys.has("__class")
142 var class_name = object["__class"]
143 assert class_name isa String
144
145 assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
146
147 # advance on path
148 path.push object
149
150 just_opened_id = id
151 var value = deserialize_class(class_name)
152 just_opened_id = null
153
154 # revert on path
155 path.pop
156
157 return value
158 end
159
160 # char?
161 if kind == "char" then
162 assert object.keys.has("__val")
163 var val = object["__val"]
164 assert val isa String
165
166 if val.length != 1 then print "Error: expected a single char when deserializing '{val}'."
167
168 return val.chars.first
169 end
170
171 print "Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
172 abort
173 end
174
175 if object isa Array[nullable Object] then
176 # special case, isa Array[nullable Serializable]
177 var array = new Array[nullable Serializable]
178 for e in object do array.add e.as(nullable Serializable)
179 return array
180 end
181
182 return object
183 end
184
185 redef fun deserialize do return convert_object(root)
186 end
187
188 redef class Serializable
189 private fun serialize_to_json(v: JsonSerializer)
190 do
191 var id = v.ref_id_for(self)
192 v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
193 core_serialize_to(v)
194 v.stream.write "\}"
195 end
196 end
197
198 redef class Int
199 redef fun serialize_to_json(v) do v.stream.write(to_s)
200 end
201
202 redef class Float
203 redef fun serialize_to_json(v) do v.stream.write(to_s)
204 end
205
206 redef class Bool
207 redef fun serialize_to_json(v) do v.stream.write(to_s)
208 end
209
210 redef class Char
211 redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
212 end
213
214 redef class String
215 redef fun serialize_to_json(v) do v.stream.write(to_json)
216 end
217
218 redef class NativeString
219 redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
220 end
221
222 redef class Collection[E]
223 # Utility to serialize a normal Json array
224 private fun serialize_to_pure_json(v: JsonSerializer)
225 do
226 v.stream.write "["
227 var is_first = true
228 for e in self do
229 if is_first then
230 is_first = false
231 else v.stream.write ", "
232
233 if not v.try_to_serialize(e) then
234 v.warn("element of type {e.class_name} is not serializable.")
235 end
236 end
237 v.stream.write "]"
238 end
239 end
240
241 redef class SimpleCollection[E]
242 redef fun serialize_to_json(v)
243 do
244 # Register as pseudo object
245 var id = v.ref_id_for(self)
246 v.stream.write """{"__kind": "obj", "__id": """
247 v.stream.write id.to_s
248 v.stream.write """, "__class": """"
249 v.stream.write class_name
250 v.stream.write """", "__length": """
251 v.stream.write length.to_s
252 v.stream.write """, "__items": """
253 serialize_to_pure_json v
254 v.stream.write "\}"
255 end
256
257 redef init from_deserializer(v: Deserializer)
258 do
259 if v isa JsonDeserializer then
260 v.notify_of_creation self
261 init
262
263 var length = v.deserialize_attribute("__length").as(Int)
264 var arr = v.path.last["__items"].as(SequenceRead[nullable Object])
265 for i in length.times do
266 var obj = v.convert_object(arr[i])
267 self.add obj
268 end
269 end
270 end
271 end
272
273 redef class Array[E]
274 redef fun serialize_to_json(v)
275 do
276 if class_name == "Array[nullable Serializable]" then
277 # Using class_name to get the exact type,
278 # we do not want Array[Int] or anything else here.
279
280 serialize_to_pure_json v
281 else super
282 end
283 end
284
285 redef class Map[K, V]
286 redef fun serialize_to_json(v)
287 do
288 # Register as pseudo object
289 var id = v.ref_id_for(self)
290
291 v.stream.write """{"__kind": "obj", "__id": """
292 v.stream.write id.to_s
293 v.stream.write """, "__class": """"
294 v.stream.write class_name
295 v.stream.write """", "__length": """
296 v.stream.write length.to_s
297 v.stream.write """, "__keys": """
298
299 keys.serialize_to_pure_json v
300
301 v.stream.write """, "__values": """
302 values.serialize_to_pure_json v
303 v.stream.write "\}"
304 end
305
306 # Instantiate a new `Array` from its serialized representation.
307 redef init from_deserializer(v: Deserializer)
308 do
309 init
310
311 if v isa JsonDeserializer then
312 v.notify_of_creation self
313
314 var length = v.deserialize_attribute("__length").as(Int)
315 var keys = v.path.last["__keys"].as(SequenceRead[nullable Object])
316 var values = v.path.last["__values"].as(SequenceRead[nullable Object])
317 for i in length.times do
318 var key = v.convert_object(keys[i])
319 var value = v.convert_object(values[i])
320 self[key] = value
321 end
322 end
323 end
324 end
325
326 # Maps instances to a value, uses `is_same_instance`
327 #
328 # Warning: This class does not implement all the services from `Map`.
329 private class StrictHashMap[K, V]
330 super Map[K, V]
331
332 # private
333 var map = new HashMap[K, Array[Couple[K, V]]]
334
335 redef var length = 0
336
337 redef fun is_empty do return length == 0
338
339 # private
340 fun node_at(key: K): nullable Couple[K, V]
341 do
342 if not map.keys.has(key) then return null
343
344 var arr = map[key]
345 for couple in arr do
346 if couple.first.is_same_serialized(key) then
347 return couple
348 end
349 end
350
351 return null
352 end
353
354 redef fun [](key)
355 do
356 var node = node_at(key)
357 assert node != null
358 return node.second
359 end
360
361 redef fun []=(key, value)
362 do
363 var node = node_at(key)
364 if node != null then
365 node.second = value
366 return
367 end
368
369 var arr
370 if not map.keys.has(key) then
371 arr = new Array[Couple[K, V]]
372 map[key] = arr
373 else arr = map[key]
374
375 arr.add new Couple[K, V](key, value)
376 self.length += 1
377 end
378
379 redef fun has_key(key) do return node_at(key) != null
380 end