1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Handles serialization and deserialization of objects to/from Json.
18 module json_serialization
23 # Serializer of Nit objects to Json string.
27 # Target writing stream
30 redef fun serialize
(object
)
32 if object
== null then
34 else object
.serialize_to_json
(self)
37 redef fun serialize_attribute
(name
, value
)
39 stream
.write
", \"{name}\
": "
43 redef fun serialize_reference
(object
)
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}\}"
55 # Map of references to already serialized objects.
56 private var refs_map
= new StrictHashMap[Serializable,Int]
58 # Get the internal serialized reference for this `object`.
59 private fun ref_id_for
(object
: Serializable): Int
61 if refs_map
.has_key
(object
) then
62 return refs_map
[object
]
64 var id
= refs_map
.length
71 # Deserializer from a Json string.
72 class JsonDeserializer
75 # Json text to deserialize from.
76 private var text
: Text
78 # Root json object parsed from input text.
79 var root
: nullable Jsonable is noinit
81 # Depth-first path in the serialized object tree.
82 var path
= new Array[JsonObject]
84 # Map of references to already deserialized objects.
85 private var id_to_object
= new StrictHashMap[Int, Object]
87 # Last encountered object reference id.
90 var just_opened_id
: nullable Int = null
93 var root
= text
.parse_json
94 if root
isa JsonObject then path
.add
(root
)
98 redef fun deserialize_attribute
(name
)
100 assert not path
.is_empty
101 var current
= path
.last
103 assert current
.keys
.has
(name
)
104 var value
= current
[name
]
106 return convert_object
(value
)
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
)
113 var id
= just_opened_id
114 if id
== null then return # Register `new_object` only once
115 id_to_object
[id
] = new_object
118 # Convert from simple Json object to Nit object
119 private fun convert_object
(object
: nullable Object): nullable Object
121 if object
isa JsonObject then
122 assert object
.keys
.has
("__kind")
123 var kind
= object
["__kind"]
126 if kind
== "ref" then
127 assert object
.keys
.has
("__id")
128 var id
= object
["__id"]
131 assert id_to_object
.has_key
(id
)
132 return id_to_object
[id
]
136 if kind
== "obj" then
137 assert object
.keys
.has
("__id")
138 var id
= object
["__id"]
141 assert object
.keys
.has
("__class")
142 var class_name
= object
["__class"]
143 assert class_name
isa String
145 assert not id_to_object
.has_key
(id
) else print
"Error: Object with id '{id}' of {class_name} is deserialized twice."
151 var value
= deserialize_class
(class_name
)
152 just_opened_id
= null
161 if kind
== "char" then
162 assert object
.keys
.has
("__val")
163 var val
= object
["__val"]
164 assert val
isa String
166 if val
.length
!= 1 then print
"Error: expected a single char when deserializing '{val}'."
168 return val
.chars
.first
171 print
"Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
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)
185 redef fun deserialize
do return convert_object
(root
)
188 redef class Serializable
189 private fun serialize_to_json
(v
: JsonSerializer)
191 var id
= v
.ref_id_for
(self)
192 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
199 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
203 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
207 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
211 redef fun serialize_to_json
(v
) do v
.stream
.write
"\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
215 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_json
)
218 redef class NativeString
219 redef fun serialize_to_json
(v
) do to_s
.serialize_to_json
(v
)
222 redef class Collection[E
]
223 # Utility to serialize a normal Json array
224 private fun serialize_to_pure_json
(v
: JsonSerializer)
231 else v
.stream
.write
", "
233 if not v
.try_to_serialize
(e
) then
234 v
.warn
("element of type {e.class_name} is not serializable.")
241 redef class SimpleCollection[E
]
242 redef fun serialize_to_json
(v
)
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
257 redef init from_deserializer(v: Deserializer)
259 if v isa JsonDeserializer then
260 v.notify_of_creation self
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])
274 redef fun serialize_to_json(v)
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.
280 serialize_to_pure_json v
285 redef class Map[K, V]
286 redef fun serialize_to_json(v)
288 # Register as pseudo object
289 var id = v.ref_id_for(self)
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
": """
299 keys.serialize_to_pure_json v
301 v.stream.write """, "__values
": """
302 values.serialize_to_pure_json v
306 # Instantiate a new `Array` from its serialized representation.
307 redef init from_deserializer(v: Deserializer)
311 if v isa JsonDeserializer then
312 v.notify_of_creation self
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])
326 # Maps instances to a value, uses `is_same_instance`
328 # Warning: This class does not implement all the services from `Map`.
329 private class StrictHashMap[K, V]
333 var map = new HashMap[K, Array[Couple[K, V]]]
337 redef fun is_empty do return length == 0
340 fun node_at(key: K): nullable Couple[K, V]
342 if not map.keys.has(key) then return null
346 if couple.first.is_same_serialized(key) then
356 var node = node_at(key)
361 redef fun []=(key, value)
363 var node = node_at(key)
370 if not map.keys.has(key) then
371 arr = new Array[Couple[K, V]]
375 arr.add new Couple[K, V](key, value)
379 redef fun has_key(key) do return node_at(key) != null