lib/json: add error checking in the json deserialization example
[nit.git] / lib / json / serialization.nit
index 813c094..19c38ea 100644 (file)
 
 # Handles serialization and deserialization of objects to/from JSON
 #
-# ## Nity JSON
+# ## Writing JSON with metadata
 #
 # `JsonSerializer` write Nit objects that subclass `Serializable` to JSON,
-# and `JsonDeserializer` can read them. They both use meta-data added to the
+# and `JsonDeserializer` can read them. They both use metadata added to the
 # generated JSON to recreate the Nit instances with the exact original type.
 #
 # For more information on Nit serialization, see: ../serialization/README.md
 #
-# ## Plain JSON
+# ## Writing plain JSON
 #
 # The attribute `JsonSerializer::plain_json` triggers generating plain and
 # clean JSON. This format is easier to read for an human and a non-Nit program,
 # but it cannot be fully deserialized. It can still be read by services from
 # `json::static` and `json::dynamic`.
 #
-# A shortcut to this service is provided by `Serializable::to_plain_json`.
+# A shortcut to these writing services is provided by `Serializable::serialize_to_json`.
 #
 # ### Usage Example
 #
@@ -39,7 +39,7 @@
 # import json::serialization
 #
 # class Person
-#     auto_serializable
+#     serialize
 #
 #     var name: String
 #     var year_of_birth: Int
 # var bob = new Person("Bob", 1986)
 # var alice = new Person("Alice", 1978, bob)
 #
-# assert bob.to_plain_json == """
-# {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}"""
+# assert bob.serialize_to_json(pretty=true, plain=true) == """
+#{
+#      "name": "Bob",
+#      "year_of_birth": 1986,
+#      "next_of_kin": null
+#}"""
 #
-# assert alice.to_plain_json == """
-# {"name": "Alice", "year_of_birth": 1978, "next_of_kin": {"name": "Bob", "year_of_birth": 1986, "next_of_kin": null}}"""
+# assert alice.serialize_to_json(pretty=true, plain=true) == """
+#{
+#      "name": "Alice",
+#      "year_of_birth": 1978,
+#      "next_of_kin": {
+#              "name": "Bob",
+#              "year_of_birth": 1986,
+#              "next_of_kin": null
+#      }
+#}"""
+# ~~~
+#
+# ## JSON to Nit objects
+#
+# The `JsonDeserializer` support reading JSON code with minimal metadata
+# to easily create Nit object from client-side code or configuration files.
+# Each JSON object must define the `__class` attribute with the corresponding
+# Nit class and the expected attributes with its name in Nit followed by its value.
+#
+# ### Usage Example
+#
+# ~~~nitish
+# import json::serialization
+#
+# class MeetupConfig
+#     serialize
+#
+#     var description: String
+#     var max_participants: nullable Int
+#     var answers: Array[FlatString]
+# end
+#
+# var json_code = """
+# {"__class": "MeetupConfig", "description": "My Awesome Meetup", "max_participants": null, "answers": ["Pepperoni", "Chicken"]}"""
+# var deserializer = new JsonDeserializer(json_code)
+#
+# var meet = deserializer.deserialize
+#
+# # Check for errors
+# assert deserializer.errors.is_empty
+#
+# assert meet isa MeetupConfig
+# assert meet.description == "My Awesome Meetup"
+# assert meet.max_participants == null
+# assert meet.answers == ["Pepperoni", "Chicken"]
 # ~~~
 module serialization
 
-import ::serialization
+import ::serialization::caching
 private import ::serialization::engine_tools
 private import static
 
 # Serializer of Nit objects to Json string.
 class JsonSerializer
-       super Serializer
+       super CachingSerializer
 
        # Target writing stream
        var stream: Writer
 
-       # Write plain JSON? easier to read but does not support Nit deserialization
+       # Write plain JSON? Standard JSON without metadata for deserialization
        #
        # If `false`, the default, serialize to support deserialization:
        #
-       # * Write meta-data, including the types of the serialized objects so they can
+       # * Write metadata, including the types of the serialized objects so they can
        #   be deserialized to their original form using `JsonDeserializer`.
        # * Use references when an object has already been serialized so to not duplicate it.
        # * Support cycles in references.
        # * Preserve the Nit `Char` type as an object because it does not exist in JSON.
        # * The generated JSON is standard and can be read by non-Nit programs.
        #   However, some Nit types are not represented by the simplest possible JSON representation.
-       #   With the added meta-data, it can be complex to read.
+       #   With the added metadata, it can be complex to read.
        #
        # If `true`, serialize for other programs:
        #
@@ -88,11 +135,20 @@ class JsonSerializer
        # * Nit objects are serialized for every references, so they can be duplicated.
        #   It is easier to read but it creates a larger output.
        # * Does not support cycles, will replace the problematic references by `null`.
-       # * Does not serialize the meta-data needed to deserialize the objects
+       # * Does not serialize the metadata needed to deserialize the objects
        #   back to regular Nit objects.
-       # * Keys of Nit `HashMap` are converted to their string reprensentation using `to_s`.
+       # * Keys of Nit `HashMap` are converted to their string representation using `to_s`.
        var plain_json = false is writable
 
+       # Write pretty JSON for human eyes?
+       #
+       # Toggles skipping lines between attributes of an object and
+       # properly indent the written JSON.
+       var pretty_json = false is writable
+
+       # Current indentation level used for writing `pretty_json`
+       private var indent_level = 0
+
        # List of the current open objects, the first is the main target of the serialization
        #
        # Used only when `plain_json == true` to detect cycles in serialization.
@@ -111,7 +167,8 @@ class JsonSerializer
                        if plain_json then
                                for o in open_objects do
                                        if object.is_same_serialized(o) then
-                                               # Cycle detected
+                                               # Cycle, can't be managed in plain json
+                                               warn "Cycle detected in serialized object, replacing reference with 'null'."
                                                stream.write "null"
                                                return
                                        end
@@ -121,7 +178,7 @@ class JsonSerializer
                        end
 
                        first_attribute = true
-                       object.serialize_to_json self
+                       object.accept_json_serializer self
                        first_attribute = false
 
                        if plain_json then open_objects.pop
@@ -131,10 +188,11 @@ class JsonSerializer
        redef fun serialize_attribute(name, value)
        do
                if not plain_json or not first_attribute then
-                       stream.write ", "
+                       stream.write ","
                        first_attribute = false
                end
 
+               new_line_and_indent
                stream.write "\""
                stream.write name
                stream.write "\": "
@@ -143,11 +201,16 @@ class JsonSerializer
 
        redef fun serialize_reference(object)
        do
-               if not plain_json and refs_map.has_key(object) then
+               if not plain_json and cache.has_object(object) then
                        # if already serialized, add local reference
-                       var id = ref_id_for(object)
-                       stream.write "\{\"__kind\": \"ref\", \"__id\": "
+                       var id = cache.id_for(object)
+                       stream.write "\{"
+                       indent_level += 1
+                       new_line_and_indent
+                       stream.write "\"__kind\": \"ref\", \"__id\": "
                        stream.write id.to_s
+                       indent_level -= 1
+                       new_line_and_indent
                        stream.write "\}"
                else
                        # serialize here
@@ -155,37 +218,28 @@ class JsonSerializer
                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
+       # Write a new line and indent it, only if `pretty_json`
+       private fun new_line_and_indent
        do
-               if refs_map.has_key(object) then
-                       return refs_map[object]
-               else
-                       var id = refs_map.length
-                       refs_map[object] = id
-                       return id
+               if pretty_json then
+                       stream.write "\n"
+                       for i in indent_level.times do stream.write "\t"
                end
        end
 end
 
 # Deserializer from a Json string.
 class JsonDeserializer
-       super Deserializer
+       super CachingDeserializer
 
        # Json text to deserialize from.
        private var text: Text
 
        # Root json object parsed from input text.
-       private var root: nullable Jsonable is noinit
+       private var root: nullable Object is noinit
 
        # Depth-first path in the serialized object tree.
-       private var path = new Array[JsonObject]
-
-       # Map of references to already deserialized objects.
-       private var id_to_object = new StrictHashMap[Int, Object]
+       private var path = new Array[Map[String, nullable Object]]
 
        # Last encountered object reference id.
        #
@@ -194,16 +248,20 @@ class JsonDeserializer
 
        init do
                var root = text.parse_json
-               if root isa JsonObject then path.add(root)
+               if root isa Map[String, nullable Object] then path.add(root)
                self.root = root
        end
 
        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)
@@ -215,37 +273,76 @@ class JsonDeserializer
        do
                var id = just_opened_id
                if id == null then return # Register `new_object` only once
-               id_to_object[id] = new_object
+               cache[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"]
+               if object isa JsonParseError then
+                       errors.add object
+                       return null
+               end
+
+               if object isa Map[String, nullable Object] then
+                       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
 
-                               assert id_to_object.has_key(id)
-                               return id_to_object[id]
+                               if not cache.has_id(id) then
+                                       errors.add new Error("Serialization Error: JSON object reference has an unknown `__id`.")
+                                       return object
+                               end
+
+                               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
+
+                                       if cache.has_id(id) then
+                                               errors.add new Error("Serialization Error: JSON object with `__id` {id} is deserialized twice.")
+                                               # Keep going
+                                       end
+                               end
 
-                               assert object.keys.has("__class")
-                               var class_name = object["__class"]
-                               assert class_name isa String
+                               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
 
-                               assert not id_to_object.has_key(id) else print "Error: Object with id '{id}' of {class_name} is deserialized twice."
+                               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
@@ -262,38 +359,199 @@ 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
 
+               # Simple JSON array without serialization metadata
                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)
+                       var array = new Array[nullable Object]
+                       var types = new HashSet[String]
+                       var has_nullable = false
+                       for e in object do
+                               var res = convert_object(e)
+                               array.add res
+
+                               if res != null then
+                                       types.add res.class_name
+                               else has_nullable = true
+                       end
+
+                       if types.length == 1 then
+                               var array_type = types.first
+
+                               var typed_array
+                               if array_type == "ASCIIFlatString" or array_type == "UnicodeFlatString" then
+                                       if has_nullable then
+                                               typed_array = new Array[nullable FlatString]
+                                       else typed_array = new Array[FlatString]
+                               else if array_type == "Int" then
+                                       if has_nullable then
+                                               typed_array = new Array[nullable Int]
+                                       else typed_array = new Array[Int]
+                               else if array_type == "Float" then
+                                       if has_nullable then
+                                               typed_array = new Array[nullable Float]
+                                       else typed_array = new Array[Float]
+                               else
+                                       # TODO support all array types when we separate the constructor
+                                       # `from_deserializer` from the filling of the items.
+
+                                       if not has_nullable then
+                                               typed_array = new Array[Object]
+                                       else
+                                               # Unsupported array type, return as `Array[nullable Object]`
+                                               return array
+                                       end
+                               end
+
+                               assert typed_array isa Array[nullable Object]
+
+                               # Copy item to the new array
+                               for e in array do typed_array.add e
+                               return typed_array
+                       end
+
+                       # Uninferable type, return as `Array[nullable Object]`
                        return array
                end
 
                return object
        end
 
-       redef fun deserialize do return convert_object(root)
+       redef fun deserialize
+       do
+               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: Map[String, nullable Object]): 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 accept_json_serializer(v) do v.stream.write(to_json)
 end
 
 redef class Serializable
-       private fun serialize_to_json(v: JsonSerializer)
+
+       # Serialize `self` to JSON
+       #
+       # Set `plain = true` to generate standard JSON, without deserialization metadata.
+       # Use this option if the generated JSON will be read by other programs or humans.
+       # Use the default, `plain = false`, if the JSON is to be deserialized by a Nit program.
+       #
+       # Set `pretty = true` to generate pretty JSON for human eyes.
+       # Use the default, `pretty = false`, to generate minified JSON.
+       #
+       # This method should not be refined by subclasses,
+       # instead `accept_json_serializer` can customize the serialization of an object.
+       #
+       # See: `JsonSerializer`
+       fun serialize_to_json(plain, pretty: nullable Bool): String
+       do
+               var stream = new StringWriter
+               var serializer = new JsonSerializer(stream)
+               serializer.plain_json = plain or else false
+               serializer.pretty_json = pretty or else false
+               serializer.serialize self
+               stream.close
+               return stream.to_s
+       end
+
+       # Refinable service to customize the serialization of this class to JSON
+       #
+       # This method can be refined to customize the serialization by either
+       # writing pure JSON directly on the stream `v.stream` or
+       # by using other services of `JsonSerializer`.
+       #
+       # Most of the time, it is preferable to refine the method `core_serialize_to`
+       # which is used by all the serialization engines, not just JSON.
+       protected fun accept_json_serializer(v: JsonSerializer)
        do
-               var id = v.ref_id_for(self)
+               var id = v.cache.new_id_for(self)
                v.stream.write "\{"
+               v.indent_level += 1
                if not v.plain_json then
+                       v.new_line_and_indent
                        v.stream.write "\"__kind\": \"obj\", \"__id\": "
                        v.stream.write id.to_s
                        v.stream.write ", \"__class\": \""
@@ -301,55 +559,40 @@ redef class Serializable
                        v.stream.write "\""
                end
                core_serialize_to(v)
-               v.stream.write "\}"
-       end
 
-       # Serialize this object to plain JSON
-       #
-       # This is a shortcut using `JsonSerializer::plain_json`,
-       # see its documentation for more information.
-       fun to_plain_json: String
-       do
-               var stream = new StringWriter
-               var serializer = new JsonSerializer(stream)
-               serializer.plain_json = true
-               serializer.serialize self
-               stream.close
-               return stream.to_s
+               v.indent_level -= 1
+               v.new_line_and_indent
+               v.stream.write "\}"
        end
 end
 
 redef class Int
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Float
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Bool
-       redef fun serialize_to_json(v) do v.stream.write(to_s)
+       redef fun accept_json_serializer(v) do v.stream.write to_s
 end
 
 redef class Char
-       redef fun serialize_to_json(v)
+       redef fun accept_json_serializer(v)
        do
                if v.plain_json then
-                       v.stream.write to_s.to_json
+                       to_s.accept_json_serializer v
                else
                        v.stream.write "\{\"__kind\": \"char\", \"__val\": "
-                       v.stream.write to_s.to_json
+                       to_s.accept_json_serializer v
                        v.stream.write "\}"
                end
        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)
+       redef fun accept_json_serializer(v) do to_s.accept_json_serializer(v)
 end
 
 redef class Collection[E]
@@ -357,115 +600,124 @@ redef class Collection[E]
        private fun serialize_to_pure_json(v: JsonSerializer)
        do
                        v.stream.write "["
+                       v.indent_level += 1
                        var is_first = true
                        for e in self do
                                if is_first then
                                        is_first = false
-                               else v.stream.write ", "
+                               else v.stream.write ","
+                               v.new_line_and_indent
 
                                if not v.try_to_serialize(e) then
+                                       assert e != null # null would have been serialized
                                        v.warn("element of type {e.class_name} is not serializable.")
                                end
                        end
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "]"
        end
 end
 
 redef class SimpleCollection[E]
-       redef fun serialize_to_json(v)
+       redef fun accept_json_serializer(v)
        do
                # Register as pseudo object
                if not v.plain_json then
-                       var id = v.ref_id_for(self)
-                       v.stream.write """{"__kind": "obj", "__id": """
+                       var id = v.cache.new_id_for(self)
+                       v.stream.write """{"""
+                       v.indent_level += 1
+                       v.new_line_and_indent
+                       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": """
+                       v.stream.write """","""
+                       v.new_line_and_indent
+                       v.stream.write """"__items": """
                end
 
                serialize_to_pure_json v
 
                if not v.plain_json then
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                end
        end
 
-       redef init from_deserializer(v: Deserializer)
+       redef init from_deserializer(v)
        do
                super
                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])
+                       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)
+       redef fun accept_json_serializer(v)
        do
                # Register as pseudo object
-               var id = v.ref_id_for(self)
+               var id = v.cache.new_id_for(self)
 
                if v.plain_json then
                        v.stream.write "\{"
+                       v.indent_level += 1
                        var first = true
                        for key, val in self do
                                if not first then
-                                       v.stream.write ", "
+                                       v.stream.write ","
                                else first = false
+                               v.new_line_and_indent
 
-                               if key == null then key = "null"
-
-                               v.stream.write key.to_s.to_json
+                               var k = key or else "null"
+                               k.to_s.accept_json_serializer v
                                v.stream.write ": "
                                if not v.try_to_serialize(val) then
+                                       assert val != null # null would have been serialized
                                        v.warn("element of type {val.class_name} is not serializable.")
                                        v.stream.write "null"
                                end
                        end
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                else
-                       v.stream.write """{"__kind": "obj", "__id": """
+                       v.stream.write "\{"
+                       v.indent_level += 1
+                       v.new_line_and_indent
+                       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": """
+                       v.stream.write ","
+                       v.new_line_and_indent
+                       v.stream.write """"__keys": """
                        keys.serialize_to_pure_json v
 
-                       v.stream.write """, "__values": """
+                       v.stream.write ","
+                       v.new_line_and_indent
+                       v.stream.write """"__values": """
                        values.serialize_to_pure_json v
 
+                       v.indent_level -= 1
+                       v.new_line_and_indent
                        v.stream.write "\}"
                end
        end
 
-       # Instantiate a new `Array` from its serialized representation.
-       redef init from_deserializer(v: Deserializer)
+       redef init from_deserializer(v)
        do
                super