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
21 # `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
22 # and `JsonDeserializer` can read them. They both use meta-data added to the
23 # generated JSON to recreate the Nit instances with the exact original type.
25 # For more information on Nit serialization, see: ../serialization/README.md
29 # The attribute `JsonSerializer::plain_json` triggers generating plain and
30 # clean JSON. This format is easier to read for an human and a non-Nit program,
31 # but it cannot be fully deserialized. It can still be read by services from
32 # `json::static` and `json::dynamic`.
34 # A shortcut to this service is provided by `Serializable::to_plain_json`.
39 # import json::serialization
45 # var year_of_birth: Int
46 # var next_of_kin: nullable Person
49 # var bob = new Person("Bob", 1986)
50 # var alice = new Person("Alice", 1978, bob)
52 # assert bob.to_plain_json == """
53 # {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
55 # assert alice.to_plain_json == """
56 # {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
60 import ::serialization
::caching
61 private import ::serialization
::engine_tools
64 # Serializer of Nit objects to Json string.
66 super CachingSerializer
68 # Target writing stream
71 # Write plain JSON? easier to read but does not support Nit deserialization
73 # If `false`, the default, serialize to support deserialization:
75 # * Write meta-data, including the types of the serialized objects so they can
76 # be deserialized to their original form using `JsonDeserializer`.
77 # * Use references when an object has already been serialized so to not duplicate it.
78 # * Support cycles in references.
79 # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
80 # * The generated JSON is standard and can be read by non-Nit programs.
81 # However, some Nit types are not represented by the simplest possible JSON representation.
82 # With the added meta-data, it can be complex to read.
84 # If `true`, serialize for other programs:
86 # * Nit objects are serialized to pure and standard JSON so they can
87 # be easily read by non-Nit programs and humans.
88 # * Nit objects are serialized for every references, so they can be duplicated.
89 # It is easier to read but it creates a larger output.
90 # * Does not support cycles, will replace the problematic references by `null`.
91 # * Does not serialize the meta-data needed to deserialize the objects
92 # back to regular Nit objects.
93 # * Keys of Nit `HashMap` are converted to their string reprensentation using `to_s`.
94 var plain_json
= false is writable
96 # List of the current open objects, the first is the main target of the serialization
98 # Used only when `plain_json == true` to detect cycles in serialization.
99 private var open_objects
= new Array[Object]
101 # Has the first attribute of the current object already been serialized?
103 # Used only when `plain_json == true`.
104 private var first_attribute
= false
106 redef fun serialize
(object
)
108 if object
== null then
112 for o
in open_objects
do
113 if object
.is_same_serialized
(o
) then
120 open_objects
.add object
123 first_attribute
= true
124 object
.serialize_to_json
self
125 first_attribute
= false
127 if plain_json
then open_objects
.pop
131 redef fun serialize_attribute
(name
, value
)
133 if not plain_json
or not first_attribute
then
135 first_attribute
= false
144 redef fun serialize_reference
(object
)
146 if not plain_json
and cache
.has_object
(object
) then
147 # if already serialized, add local reference
148 var id
= cache
.id_for
(object
)
149 stream
.write
"\{\"__kind\
": \"ref\
", \"__id\
": "
159 # Deserializer from a Json string.
160 class JsonDeserializer
161 super CachingDeserializer
163 # Json text to deserialize from.
164 private var text
: Text
166 # Root json object parsed from input text.
167 private var root
: nullable Jsonable is noinit
169 # Depth-first path in the serialized object tree.
170 private var path
= new Array[JsonObject]
172 # Last encountered object reference id.
174 # See `id_to_object`.
175 var just_opened_id
: nullable Int = null
178 var root
= text
.parse_json
179 if root
isa JsonObject then path
.add
(root
)
183 redef fun deserialize_attribute
(name
)
185 assert not path
.is_empty
# This is an internal error, abort
186 var current
= path
.last
188 if not current
.keys
.has
(name
) then
189 errors
.add
new Error("Deserialization Error: JSON object has not attribute '{name}'.")
193 var value
= current
[name
]
195 return convert_object
(value
)
198 # This may be called multiple times by the same object from constructors
199 # in different nclassdef
200 redef fun notify_of_creation
(new_object
)
202 var id
= just_opened_id
203 if id
== null then return # Register `new_object` only once
204 cache
[id
] = new_object
207 # Convert from simple Json object to Nit object
208 private fun convert_object
(object
: nullable Object): nullable Object
210 if object
isa JsonParseError then
215 if object
isa JsonObject then
217 if object
.keys
.has
("__kind") then
218 kind
= object
["__kind"]
222 if kind
== "ref" then
223 if not object
.keys
.has
("__id") then
224 errors
.add
new Error("Serialization Error: JSON object reference does not declare a `__id`.")
228 var id
= object
["__id"]
229 if not id
isa Int then
230 errors
.add
new Error("Serialization Error: JSON object reference declares a non-integer `__id`.")
234 if not cache
.has_id
(id
) then
235 errors
.add
new Error("Serialization Error: JSON object reference has an unknown `__id`.")
239 return cache
.object_for
(id
)
243 if kind
== "obj" or kind
== null then
245 if object
.keys
.has
("__id") then
248 if not id
isa Int then
249 errors
.add
new Error("Serialization Error: JSON object declaration declares a non-integer `__id`.")
253 if cache
.has_id
(id
) then
254 errors
.add
new Error("Serialization Error: JSON object with `__id` {id} is deserialized twice.")
259 if not object
.keys
.has
("__class") then
260 errors
.add
new Error("Serialization Error: JSON object declaration does not declare a `__class`.")
264 var class_name
= object
["__class"]
265 if not class_name
isa String then
266 errors
.add
new Error("Serialization Error: JSON object declaration declares a non-string `__class`.")
274 var value
= deserialize_class
(class_name
)
275 just_opened_id
= null
284 if kind
== "char" then
285 if not object
.keys
.has
("__val") then
286 errors
.add
new Error("Serialization Error: JSON `char` object does not declare a `__val`.")
290 var val
= object
["__val"]
292 if not val
isa String or val
.is_empty
then
293 errors
.add
new Error("Serialization Error: JSON `char` object does not declare a single char in `__val`.")
297 return val
.chars
.first
300 errors
.add
new Error("Serialization Error: JSON object has an unknown `__kind`.")
304 if object
isa Array[nullable Object] then
305 # special case, isa Array[nullable Serializable]
306 var array
= new Array[nullable Serializable]
307 for e
in object
do array
.add e
.as(nullable Serializable)
314 redef fun deserialize
317 return convert_object
(root
)
321 redef class Serializable
322 private fun serialize_to_json
(v
: JsonSerializer)
324 var id
= v
.cache
.new_id_for
(self)
326 if not v
.plain_json
then
327 v
.stream
.write
"\"__kind\
": \"obj\
", \"__id\
": "
328 v
.stream
.write id
.to_s
329 v
.stream
.write
", \"__class\
": \""
330 v.stream.write class_name
337 # Serialize this object to plain JSON
339 # This is a shortcut using `JsonSerializer::plain_json`,
340 # see its documentation for more information.
341 fun to_plain_json
: String
343 var stream
= new StringWriter
344 var serializer
= new JsonSerializer(stream
)
345 serializer
.plain_json
= true
346 serializer
.serialize
self
353 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
357 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
361 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
365 redef fun serialize_to_json
(v
)
368 v
.stream
.write to_s
.to_json
370 v
.stream
.write
"\{\"__kind\
": \"char\
", \"__val\
": "
371 v
.stream
.write to_s
.to_json
378 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_json
)
381 redef class NativeString
382 redef fun serialize_to_json
(v
) do to_s
.serialize_to_json
(v
)
385 redef class Collection[E
]
386 # Utility to serialize a normal Json array
387 private fun serialize_to_pure_json
(v
: JsonSerializer)
394 else v
.stream
.write
", "
396 if not v
.try_to_serialize
(e
) then
397 v
.warn
("element of type {e.class_name} is not serializable.")
404 redef class SimpleCollection[E
]
405 redef fun serialize_to_json
(v
)
407 # Register as pseudo object
408 if not v
.plain_json
then
409 var id
= v
.cache
.new_id_for
(self)
410 v
.stream
.write
"""{"__kind": "obj", "__id": """
411 v
.stream
.write id
.to_s
412 v
.stream
.write
""", "__class": """"
413 v.stream.write class_name
414 v.stream.write """", "__items": """
417 serialize_to_pure_json v
419 if not v.plain_json then
424 redef init from_deserializer(v: Deserializer)
427 if v isa JsonDeserializer then
428 v.notify_of_creation self
431 var arr = v.path.last["__items"].as(SequenceRead[nullable Object])
433 var obj = v.convert_object(o)
441 redef fun serialize_to_json(v)
443 if v.plain_json or class_name == "Array[nullable Serializable]" then
444 # Using class_name to get the exact type,
445 # we do not want Array[Int] or anything else here.
447 serialize_to_pure_json v
452 redef class Map[K, V]
453 redef fun serialize_to_json(v)
455 # Register as pseudo object
456 var id = v.cache.new_id_for(self)
461 for key, val in self do
466 if key == null then key = "null"
468 v.stream.write key.to_s.to_json
470 if not v.try_to_serialize(val) then
471 v.warn("element of type {val.class_name} is not serializable.")
472 v.stream.write "null"
477 v.stream.write """{"__kind": "obj", "__id": """
478 v.stream.write id.to_s
479 v.stream.write """, "__class": """"
480 v.stream.write class_name
481 v.stream.write """", "__length
": """
482 v.stream.write length.to_s
484 v.stream.write """, "__keys
": """
485 keys.serialize_to_pure_json v
487 v.stream.write """, "__values
": """
488 values.serialize_to_pure_json v
494 # Instantiate a new `Array` from its serialized representation.
495 redef init from_deserializer(v: Deserializer)
499 if v isa JsonDeserializer then
500 v.notify_of_creation self
503 var length = v.deserialize_attribute("__length
").as(Int)
504 var keys = v.path.last["__keys
"].as(SequenceRead[nullable Object])
505 var values = v.path.last["__values
"].as(SequenceRead[nullable Object])
506 for i in length.times do
507 var key = v.convert_object(keys[i])
508 var value = v.convert_object(values[i])