# import json::serialization
#
# class Person
-# auto_serializable
+# serialize
#
# var name: String
# var year_of_birth: Int
# ~~~
module serialization
-import ::serialization
+import ::serialization::caching
private import ::serialization::engine_tools
private import static
# Serializer of Nit objects to Json string.
class JsonSerializer
- super Serializer
+ super CachingSerializer
# Target writing stream
var stream: Writer
redef fun serialize_reference(object)
do
- if not plain_json and refs_map.has_key(object) then
+ if not plain_json and cache.has_object(object) then
# if already serialized, add local reference
- var id = ref_id_for(object)
+ var id = cache.id_for(object)
stream.write "\{\"__kind\": \"ref\", \"__id\": "
stream.write id.to_s
stream.write "\}"
serialize object
end
end
-
- # Map of references to already serialized objects.
- private var refs_map = new StrictHashMap[Serializable,Int]
-
- # Get the internal serialized reference for this `object`.
- private fun ref_id_for(object: Serializable): Int
- do
- if refs_map.has_key(object) then
- return refs_map[object]
- else
- var id = refs_map.length
- refs_map[object] = id
- return id
- end
- end
end
# Deserializer from a Json string.
class JsonDeserializer
- super Deserializer
+ super CachingDeserializer
# Json text to deserialize from.
private var text: Text
# Depth-first path in the serialized object tree.
private var path = new Array[JsonObject]
- # Map of references to already deserialized objects.
- private var id_to_object = new StrictHashMap[Int, Object]
-
# Last encountered object reference id.
#
# See `id_to_object`.
redef fun deserialize_attribute(name)
do
- assert not path.is_empty
+ assert not path.is_empty # This is an internal error, abort
var current = path.last
- assert current.keys.has(name)
+ if not current.keys.has(name) then
+ errors.add new Error("Deserialization Error: JSON object has not attribute '{name}'.")
+ return null
+ end
+
var value = current[name]
return convert_object(value)
do
var id = just_opened_id
if id == null then return # Register `new_object` only once
- id_to_object[id] = new_object
+ cache[id] = new_object
end
# Convert from simple Json object to Nit object
private fun convert_object(object: nullable Object): nullable Object
do
+ if object isa JsonParseError then
+ errors.add object
+ return null
+ end
+
if object isa JsonObject then
assert object.keys.has("__kind")
var kind = object["__kind"]
# ref?
if kind == "ref" then
- assert object.keys.has("__id")
+ if not object.keys.has("__id") then
+ errors.add new Error("Serialization Error: JSON object reference does not declare a `__id`.")
+ return object
+ end
+
var id = object["__id"]
- assert id isa Int
+ if not id isa Int then
+ errors.add new Error("Serialization Error: JSON object reference declares a non-integer `__id`.")
+ return object
+ end
+
+ if not cache.has_id(id) then
+ errors.add new Error("Serialization Error: JSON object reference has an unknown `__id`.")
+ return object
+ end
- assert id_to_object.has_key(id)
- return id_to_object[id]
+ return cache.object_for(id)
end
# obj?
if kind == "obj" then
- assert object.keys.has("__id")
- var id = object["__id"]
- assert id isa Int
+ var id = null
+ if object.keys.has("__id") then
+ id = object["__id"]
- assert object.keys.has("__class")
- var class_name = object["__class"]
- assert class_name isa String
+ if not id isa Int then
+ errors.add new Error("Serialization Error: JSON object declaration declares a non-integer `__id`.")
+ return object
+ end
+
+ if cache.has_id(id) then
+ errors.add new Error("Serialization Error: JSON object with `__id` {id} is deserialized twice.")
+ # Keep going
+ end
+ end
+
+ if not object.keys.has("__class") then
+ errors.add new Error("Serialization Error: JSON object declaration does not declare a `__class`.")
+ return object
+ end
- assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
+ var class_name = object["__class"]
+ if not class_name isa String then
+ errors.add new Error("Serialization Error: JSON object declaration declares a non-string `__class`.")
+ return object
+ end
# advance on path
path.push object
# char?
if kind == "char" then
- assert object.keys.has("__val")
+ if not object.keys.has("__val") then
+ errors.add new Error("Serialization Error: JSON `char` object does not declare a `__val`.")
+ return object
+ end
+
var val = object["__val"]
- assert val isa String
- if val.length != 1 then print "Error: expected a single char when deserializing '{val}'."
+ if not val isa String or val.is_empty then
+ errors.add new Error("Serialization Error: JSON `char` object does not declare a single char in `__val`.")
+ return object
+ end
return val.chars.first
end
- print "Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
- abort
+ errors.add new Error("Serialization Error: JSON object has an unknown `__kind`.")
+ return object
end
if object isa Array[nullable Object] then
redef class Serializable
private fun serialize_to_json(v: JsonSerializer)
do
- var id = v.ref_id_for(self)
+ var id = v.cache.new_id_for(self)
v.stream.write "\{"
if not v.plain_json then
v.stream.write "\"__kind\": \"obj\", \"__id\": "
do
# Register as pseudo object
if not v.plain_json then
- var id = v.ref_id_for(self)
+ var id = v.cache.new_id_for(self)
v.stream.write """{"__kind": "obj", "__id": """
v.stream.write id.to_s
v.stream.write """, "__class": """"
redef init from_deserializer(v: Deserializer)
do
+ super
if v isa JsonDeserializer then
v.notify_of_creation self
init
redef fun serialize_to_json(v)
do
# Register as pseudo object
- var id = v.ref_id_for(self)
+ var id = v.cache.new_id_for(self)
if v.plain_json then
v.stream.write "\{"
# Instantiate a new `Array` from its serialized representation.
redef init from_deserializer(v: Deserializer)
do
- init
+ super
if v isa JsonDeserializer then
v.notify_of_creation self
+ init
var length = v.deserialize_attribute("__length").as(Int)
var keys = v.path.last["__keys"].as(SequenceRead[nullable Object])