X-Git-Url: http://nitlanguage.org diff --git a/lib/json/serialization.nit b/lib/json/serialization.nit index 61c6f61..41018a0 100644 --- a/lib/json/serialization.nit +++ b/lib/json/serialization.nit @@ -39,7 +39,7 @@ # import json::serialization # # class Person -# auto_serializable +# serialize # # var name: String # var year_of_birth: Int @@ -57,13 +57,13 @@ # ~~~ 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 @@ -143,9 +143,9 @@ class JsonSerializer 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 "\}" @@ -154,26 +154,11 @@ class JsonSerializer 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 @@ -184,9 +169,6 @@ class JsonDeserializer # 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`. @@ -200,10 +182,14 @@ class JsonDeserializer 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) @@ -215,7 +201,7 @@ class JsonDeserializer 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 @@ -227,25 +213,52 @@ class JsonDeserializer # 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 - assert id_to_object.has_key(id) - return id_to_object[id] + if not cache.has_id(id) then + errors.add new Error("Serialization Error: JSON object reference has an unknown `__id`.") + return object + end + + 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 - assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice." + if not object.keys.has("__class") then + errors.add new Error("Serialization Error: JSON object declaration does not declare a `__class`.") + return object + end + + 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 @@ -262,17 +275,23 @@ class JsonDeserializer # 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 @@ -291,7 +310,7 @@ end 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\": " @@ -376,7 +395,7 @@ redef class SimpleCollection[E] 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": """" @@ -395,6 +414,7 @@ redef class SimpleCollection[E] redef init from_deserializer(v: Deserializer) do + super if v isa JsonDeserializer then v.notify_of_creation self init @@ -425,7 +445,7 @@ redef class Map[K, V] 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 "\{" @@ -466,10 +486,11 @@ redef class Map[K, V] # 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])