X-Git-Url: http://nitlanguage.org diff --git a/lib/json_serialization.nit b/lib/json_serialization.nit deleted file mode 100644 index b9667db..0000000 --- a/lib/json_serialization.nit +++ /dev/null @@ -1,380 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2014 Alexis Laferrière -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# 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 json::static - -# Serializer of Nit objects to Json string. -class JsonSerializer - super Serializer - - # Target writing stream - var stream: Writer - - redef fun serialize(object) - do - if object == null then - stream.write "null" - else object.serialize_to_json(self) - end - - redef fun serialize_attribute(name, value) - do - stream.write ", \"{name}\": " - super - end - - redef fun serialize_reference(object) - do - if refs_map.has_key(object) then - # if already serialized, add local reference - var id = ref_id_for(object) - stream.write "\{\"__kind\": \"ref\", \"__id\": {id}\}" - else - # serialize here - 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 - - # 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 references to already deserialized objects. - private var id_to_object = new StrictHashMap[Int, Object] - - # Last encountered object reference id. - # - # See `id_to_object`. - var just_opened_id: nullable Int = null - - init do - var root = text.parse_json - if root isa JsonObject then path.add(root) - self.root = root - end - - redef fun deserialize_attribute(name) - do - assert not path.is_empty - var current = path.last - - assert current.keys.has(name) - var value = current[name] - - return convert_object(value) - end - - # This may be called multiple times by the same object from constructors - # in different nclassdef - redef fun notify_of_creation(new_object) - do - var id = just_opened_id - if id == null then return # Register `new_object` only once - id_to_object[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 JsonObject then - assert object.keys.has("__kind") - var kind = object["__kind"] - - # ref? - if kind == "ref" then - assert object.keys.has("__id") - var id = object["__id"] - assert id isa Int - - assert id_to_object.has_key(id) - return id_to_object[id] - end - - # obj? - if kind == "obj" then - assert object.keys.has("__id") - var id = object["__id"] - assert id isa Int - - assert object.keys.has("__class") - var class_name = object["__class"] - assert class_name isa String - - assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice." - - # advance on path - path.push object - - just_opened_id = id - var value = deserialize_class(class_name) - just_opened_id = null - - # revert on path - path.pop - - return value - end - - # 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}'." - - 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 - - redef fun deserialize do return convert_object(root) -end - -redef class Serializable - private fun serialize_to_json(v: JsonSerializer) - do - var id = v.ref_id_for(self) - v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\"" - core_serialize_to(v) - v.stream.write "\}" - end -end - -redef class Int - redef fun serialize_to_json(v) do v.stream.write(to_s) -end - -redef class Float - redef fun serialize_to_json(v) do v.stream.write(to_s) -end - -redef class Bool - redef fun serialize_to_json(v) do v.stream.write(to_s) -end - -redef class Char - 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) -end - -redef class NativeString - redef fun serialize_to_json(v) do to_s.serialize_to_json(v) -end - -redef class Collection[E] - # Utility to serialize a normal Json array - private fun serialize_to_pure_json(v: JsonSerializer) - 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.") - end - end - v.stream.write "]" - end -end - -redef class SimpleCollection[E] - redef fun serialize_to_json(v) - do - # Register as pseudo object - var id = v.ref_id_for(self) - v.stream.write """{"__kind": "obj", "__id": """ - v.stream.write id.to_s - v.stream.write """, "__class": """" - v.stream.write class_name - v.stream.write """", "__length": """ - v.stream.write length.to_s - v.stream.write """, "__items": """ - serialize_to_pure_json v - v.stream.write "\}" - end - - redef init from_deserializer(v: Deserializer) - do - if v isa JsonDeserializer then - v.notify_of_creation self - init - - 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 - end -end - -redef class Array[E] - redef fun serialize_to_json(v) - do - if class_name == "Array[nullable Serializable]" then - # Using class_name to get the exact type, - # we do not want Array[Int] or anything else here. - - serialize_to_pure_json v - else super - end -end - -redef class Map[K, V] - redef fun serialize_to_json(v) - do - # Register as pseudo object - var id = v.ref_id_for(self) - - v.stream.write """{"__kind": "obj", "__id": """ - v.stream.write id.to_s - v.stream.write """, "__class": """" - v.stream.write class_name - v.stream.write """", "__length": """ - v.stream.write length.to_s - v.stream.write """, "__keys": """ - - keys.serialize_to_pure_json v - - v.stream.write """, "__values": """ - values.serialize_to_pure_json v - v.stream.write "\}" - end - - # Instantiate a new `Array` from its serialized representation. - redef init from_deserializer(v: Deserializer) - do - init - - if v isa JsonDeserializer then - v.notify_of_creation self - - var length = v.deserialize_attribute("__length").as(Int) - var keys = v.path.last["__keys"].as(SequenceRead[nullable Object]) - var values = v.path.last["__values"].as(SequenceRead[nullable Object]) - for i in length.times do - var key = v.convert_object(keys[i]) - var value = v.convert_object(values[i]) - self[key] = value - end - end - end -end - -# Maps instances to a value, uses `is_same_instance` -# -# Warning: This class does not implement all the services from `Map`. -private class StrictHashMap[K, V] - super Map[K, V] - - # private - var map = new HashMap[K, Array[Couple[K, V]]] - - redef var length = 0 - - redef fun is_empty do return length == 0 - - # private - fun node_at(key: K): nullable Couple[K, V] - do - if not map.keys.has(key) then return null - - var arr = map[key] - for couple in arr do - if couple.first.is_same_serialized(key) then - return couple - end - end - - return null - end - - redef fun [](key) - do - var node = node_at(key) - assert node != null - return node.second - end - - redef fun []=(key, value) - do - var node = node_at(key) - if node != null then - node.second = value - return - end - - var arr - if not map.keys.has(key) then - arr = new Array[Couple[K, V]] - map[key] = arr - else arr = map[key] - - arr.add new Couple[K, V](key, value) - self.length += 1 - end - - redef fun has_key(key) do return node_at(key) != null -end