lib/json: serialize `Array[nullable Serializable]` as any other arrays
[nit.git] / lib / json / serialization.nit
index 4b7cb15..be9ee8c 100644 (file)
@@ -39,7 +39,7 @@
 # import json::serialization
 #
 # class Person
-#     auto_serializable
+#     serialize
 #
 #     var name: String
 #     var year_of_birth: Int
@@ -182,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)
@@ -203,31 +207,65 @@ class JsonDeserializer
        # Convert from simple Json object to Nit object
        private fun convert_object(object: nullable Object): nullable Object
        do
+               if object isa JsonParseError then
+                       errors.add object
+                       return null
+               end
+
                if object isa JsonObject then
-                       assert object.keys.has("__kind")
-                       var kind = object["__kind"]
+                       var kind = null
+                       if object.keys.has("__kind") then
+                               kind = object["__kind"]
+                       end
 
                        # 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
+
+                               if not cache.has_id(id) then
+                                       errors.add new Error("Serialization Error: JSON object reference has an unknown `__id`.")
+                                       return object
+                               end
 
-                               assert cache.has_id(id)
                                return cache.object_for(id)
                        end
 
                        # obj?
-                       if kind == "obj" then
-                               assert object.keys.has("__id")
-                               var id = object["__id"]
-                               assert id isa Int
+                       if kind == "obj" or kind == null then
+                               var id = null
+                               if object.keys.has("__id") then
+                                       id = object["__id"]
+
+                                       if not id isa Int then
+                                               errors.add new Error("Serialization Error: JSON object declaration declares a non-integer `__id`.")
+                                               return object
+                                       end
 
-                               assert object.keys.has("__class")
-                               var class_name = object["__class"]
-                               assert class_name isa String
+                                       if cache.has_id(id) then
+                                               errors.add new Error("Serialization Error: JSON object with `__id` {id} is deserialized twice.")
+                                               # Keep going
+                                       end
+                               end
+
+                               if not object.keys.has("__class") then
+                                       errors.add new Error("Serialization Error: JSON object declaration does not declare a `__class`.")
+                                       return object
+                               end
 
-                               assert not cache.has_id(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
+                               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
@@ -244,17 +282,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
@@ -267,7 +311,11 @@ class JsonDeserializer
                return object
        end
 
-       redef fun deserialize do return convert_object(root)
+       redef fun deserialize
+       do
+               errors.clear
+               return convert_object(root)
+       end
 end
 
 redef class Serializable
@@ -363,9 +411,7 @@ redef class SimpleCollection[E]
                        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": """
+                       v.stream.write """", "__items": """
                end
 
                serialize_to_pure_json v
@@ -382,28 +428,15 @@ redef class SimpleCollection[E]
                        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])
+                       for o in arr do
+                               var obj = v.convert_object(o)
                                self.add obj
                        end
                end
        end
 end
 
-redef class Array[E]
-       redef fun serialize_to_json(v)
-       do
-               if v.plain_json or 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