import ::serialization::caching
private import ::serialization::engine_tools
-private import static
+import static
# Serializer of Nit objects to Json string.
class JsonSerializer
end
end
- if not object.keys.has("__class") then
+ var class_name = object.get_or_null("__class")
+ if class_name == null then
+ # Fallback to custom heuristic
+ class_name = class_name_heuristic(object)
+ end
+
+ if class_name == null 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
errors.clear
return convert_object(root)
end
+
+ # User customizable heuristic to get the name of the Nit class to deserialize `json_object`
+ #
+ # This method is called only when deserializing an object without the metadata `__class`.
+ # Return the class name as a `String` when it can be inferred.
+ # Return `null` when the class name cannot be found.
+ #
+ # If a valid class name is returned, `json_object` will then be deserialized normally.
+ # So it must contain the attributes of the corresponding class, as usual.
+ #
+ # ~~~nitish
+ # class MyData
+ # serialize
+ #
+ # var data: String
+ # end
+ #
+ # class MyError
+ # serialize
+ #
+ # var error: String
+ # end
+ #
+ # class MyJsonDeserializer
+ # super JsonDeserializer
+ #
+ # redef fun class_name_heuristic(json_object)
+ # do
+ # if json_object.keys.has("error") then return "MyError"
+ # if json_object.keys.has("data") then return "MyData"
+ # return null
+ # end
+ # end
+ #
+ # var json = """{"data": "some other data"}"""
+ # var deserializer = new MyJsonDeserializer(json)
+ # var deserialized = deserializer.deserialize
+ # assert deserialized isa MyData
+ #
+ # json = """{"error": "some error message"}"""
+ # deserializer = new MyJsonDeserializer(json)
+ # deserialized = deserializer.deserialize
+ # assert deserialized isa MyError
+ # ~~~
+ protected fun class_name_heuristic(json_object: JsonObject): nullable String
+ do
+ return null
+ end
+end
+
+redef class Text
+
+ # Deserialize a `nullable Object` from this JSON formatted string
+ #
+ # Warning: Deserialization errors are reported with `print_error` and
+ # may be returned as a partial object or as `null`.
+ #
+ # This method is not appropriate when errors need to be handled programmatically,
+ # manually use a `JsonDeserializer` in such cases.
+ fun from_json_string: nullable Object
+ do
+ var deserializer = new JsonDeserializer(self)
+ var res = deserializer.deserialize
+ if deserializer.errors.not_empty then
+ print_error "Deserialization Errors: {deserializer.errors.join(", ")}"
+ end
+ return res
+ end
+
+ redef fun serialize_to_json(v) do v.stream.write(to_json)
end
redef class Serializable
v.stream.write "\}"
end
+ # Serialize this object to a JSON string with metadata for deserialization
+ fun to_json_string: String
+ do
+ var stream = new StringWriter
+ var serializer = new JsonSerializer(stream)
+ serializer.serialize self
+ stream.close
+ return stream.to_s
+ end
+
# Serialize this object to plain JSON
#
# This is a shortcut using `JsonSerializer::plain_json`,
end
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
end
end
- redef init from_deserializer(v: Deserializer)
+ redef init from_deserializer(v)
do
super
if v isa JsonDeserializer then
v.stream.write ", "
else first = false
- if key == null then key = "null"
-
- v.stream.write key.to_s.to_json
+ var k = key or else "null"
+ v.stream.write k.to_s.to_json
v.stream.write ": "
if not v.try_to_serialize(val) then
v.warn("element of type {val.class_name} is not serializable.")
end
end
- # Instantiate a new `Array` from its serialized representation.
- redef init from_deserializer(v: Deserializer)
+ redef init from_deserializer(v)
do
super