X-Git-Url: http://nitlanguage.org diff --git a/lib/json_serialization.nit b/lib/json_serialization.nit index b4b1062..e8c76b4 100644 --- a/lib/json_serialization.nit +++ b/lib/json_serialization.nit @@ -14,18 +14,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Handles serialization and deserialization of objects to/from Json. module json_serialization import serialization -import simple_json_reader +import json::static +# Serializer of Nit objects to Json string. class JsonSerializer super Serializer # Target writing stream - var stream: OStream - - init(stream: OStream) do self.stream = stream + var stream: Writer redef fun serialize(object) do @@ -52,9 +52,10 @@ class JsonSerializer end end - # Map of references to already serialized objects + # Map of references to already serialized objects. var refs_map = new HashMap[Serializable,Int] + # Get the internal serialized reference for this `object`. private fun ref_id_for(object: Serializable): Int do if refs_map.keys.has(object) then @@ -67,20 +68,30 @@ class JsonSerializer end end -# Deserializer from a Json string +# Deserializer from a Json string. class JsonDeserializer super Deserializer - var root: nullable Object - var path = new Array[HashMap[String, nullable Object]] + # Json text to deserialize from. + private var text: Text + + # Root json object parsed from input text. + var root: nullable Jsonable is noinit + + # Depth-first path in the serialized object tree. + var path = new Array[JsonObject] + + # Map of refenrences to already deserialized objects. var id_to_object = new HashMap[Int, Object] + # Last encountered object reference id. + # + # See `id_to_object`. var just_opened_id: nullable Int = null - init(text: String) - do - var root = text.json_to_nit_object - if root isa HashMap[String, nullable Object] then path.add(root) + init do + var root = text.parse_json + if root isa JsonObject then path.add(root) self.root = root end @@ -91,7 +102,7 @@ class JsonDeserializer assert current.keys.has(name) var value = current[name] - + return convert_object(value) end @@ -107,7 +118,7 @@ class JsonDeserializer # Convert from simple Json object to Nit object private fun convert_object(object: nullable Object): nullable Object do - if object isa HashMap[String, nullable Object] then + if object isa JsonObject then assert object.keys.has("__kind") var kind = object["__kind"] @@ -146,12 +157,28 @@ class JsonDeserializer return value end - # char? TODO + # char? + if kind == "char" then + assert object.keys.has("__val") + var val = object["__val"] + assert val isa String + + if val.length != 1 then print "Error: expected a single char when deserializing '{val}'." - print "Malformed Json string: unexpected Json Object kind '{kind}'" + return val.chars.first + end + + print "Malformed Json string: unexpected Json Object kind '{kind or else "null"}'" abort end + if object isa Array[nullable Object] then + # special case, isa Array[nullable Serializable] + var array = new Array[nullable Serializable] + for e in object do array.add e.as(nullable Serializable) + return array + end + return object end @@ -181,17 +208,11 @@ redef class Bool end redef class Char - redef fun serialize_to_json(v) do v.stream.write("'{to_s}'") + redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}" end redef class String - redef fun serialize_to_json(v) do v.stream.write("\"{to_json_s}\"") - - private fun to_json_s: String do return self.replace("\\", "\\\\"). - replace("\"", "\\\"").replace("\b", "\\b").replace("/", "\\/"). - replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t") - # FIXME add support for unicode char when supported by Nit strings - # FIXME add support for \f! # .replace("\f", "\\f") + redef fun serialize_to_json(v) do v.stream.write(to_json) end redef class NativeString @@ -199,18 +220,55 @@ redef class NativeString end redef class Array[E] - redef fun serialize_to_json(v) do - v.stream.write "[" - var is_first = true - for e in self do - if is_first then - is_first = false - else v.stream.write(", ") - - if not v.try_to_serialize(e) then - v.warn("element of type {e.class_name} is not serializable.") + redef fun serialize_to_json(v) + do + if class_name == "Array[nullable Serializable]" then + # Using class_name to the the exact type + # We do not want Array[Int] or anything else here + v.stream.write "[" + var is_first = true + for e in self do + if is_first then + is_first = false + else v.stream.write(", ") + + if not v.try_to_serialize(e) then + v.warn("element of type {e.class_name} is not serializable.") + end + end + v.stream.write "]" + else + # Register as pseudo object + var id = v.ref_id_for(self) + v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\"" + v.stream.write """, "__length": {{{length}}}, "__items": [""" + var is_first = true + for e in self do + if is_first then + is_first = false + else v.stream.write(", ") + + if not v.try_to_serialize(e) then + v.warn("element of type {e.class_name} is not serializable.") + end + end + v.stream.write "]" + v.stream.write "\}" + end + end + + # Instanciate a new `Array` from its serialized representation. + init from_deserializer(v: Deserializer) + do + if v isa JsonDeserializer then + v.notify_of_creation self + + var length = v.deserialize_attribute("__length").as(Int) + var arr = v.path.last["__items"].as(SequenceRead[nullable Object]) + for i in length.times do + var obj = v.convert_object(arr[i]) + self.add obj end end - v.stream.write "]" end end