+# 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 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 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
+ assert id != null
+ 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.keys.has(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.keys.has(id) else print "Error: Object with id '{id}' 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
+