1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Deserialize full Nit objects from MessagePack format
17 # See the package `msgpack` for more details on the serialization
19 module serialization_read
21 import serialization
::caching
22 import serialization
::safe
23 private import json
# for class_inheritance_metamodel
24 private import serialization
::engine_tools
26 import serialization_common
35 # Deserialize full Nit `nullable Object` from MessagePack formated data
37 # The dynamic type of the deserialized object can be limited to `static_type`.
39 # Warning: Deserialization errors are reported with `print_error`,
40 # the returned object may be partial or fall back on `null`.
41 # To handle the errors programmatically, use a `MsgPackDeserializer`.
42 fun deserialize_msgpack
(static_type
: nullable String): nullable Object
44 var stream
= new BytesReader(self)
45 var res
= stream
.deserialize_msgpack
(static_type
)
53 # Deserialize full Nit `nullable Object` from MessagePack formated data
55 # This method use metadata in the MessagePack source to recreate full
56 # Nit objects serialized by `Writer::serialize_msgpack` or
57 # `MsgPackSerializer`.
59 # The dynamic type of the deserialized object can be limited to `static_type`.
61 # Warning: Deserialization errors are reported with `print_error`,
62 # the returned object may be partial or fall back on `null`.
63 # To handle the errors programmatically, use a `MsgPackDeserializer`.
64 fun deserialize_msgpack
(static_type
: nullable String): nullable Object
66 var deserializer
= new MsgPackDeserializer(self)
67 var res
= deserializer
.deserialize
(static_type
)
69 if deserializer
.errors
.length
== 1 then
70 print_error deserializer
.errors
.join
("")
71 else if deserializer
.errors
.not_empty
then
72 print_error
"Deserialization Errors:\n* {deserializer.errors.join("\n* ")}"
82 # Deserialize MessagePack format to full Nit objects
83 class MsgPackDeserializer
84 super CachingDeserializer
86 super SafeDeserializer
91 # Map of attributes from the root deserialized object to the current object
92 private var path
= new Array[Map[nullable Serializable, nullable Serializable]]
94 # Metadata arrays with from the root deserialized object to the current object
95 var path_arrays
= new Array[nullable Array[nullable Object]]
97 # Names of the attributes from the root to the object currently being deserialized
98 var attributes_path
= new Array[String]
100 # Last encountered object reference id.
102 # See `id_to_object`.
103 private var just_opened_id
: nullable Int = null
105 redef fun deserialize_attribute
(name
, static_type
)
107 if path
.is_empty
then
108 # The was a parsing error or the root is not an object
109 deserialize_attribute_missing
= false
113 var current
= path
.last
115 var serialized_value
= null
116 var serialized_value_found
= false
117 if current
.keys
.has
(name
) then
119 serialized_value
= current
[name
]
120 serialized_value_found
= true
122 # It may be cached, deserialize all keys until we find it
123 for key
in current
.keys
.to_a
do
124 if key
isa Array[nullable Serializable] or key
isa MsgPackExt then
125 var str
= convert_object
(key
, "String")
126 if str
isa String then
127 var value
= current
[key
]
128 current
.keys
.remove key
132 serialized_value
= value
133 serialized_value_found
= true
141 if not serialized_value_found
then
142 # Let the generated code / caller of `deserialize_attribute` raise the missing attribute error
143 deserialize_attribute_missing
= true
147 attributes_path
.add name
148 var res
= convert_object
(serialized_value
, static_type
)
151 deserialize_attribute_missing
= false
155 # This may be called multiple times by the same object from defs of a same constructor
156 redef fun notify_of_creation
(new_object
)
158 var id
= just_opened_id
159 if id
== null then return
160 cache
[id
] = new_object
163 # Convert the simple JSON `object` to a Nit object
164 private fun convert_object
(object
: nullable Object, static_type
: nullable String): nullable Object
166 #print "convert_object {if object != null then object.class_name else "null"}"
167 if object
isa Array[nullable Object] and object
.length
>= 1 then
169 var first
= object
.first
170 if first
isa MsgPackExt then
171 if first
.typ
== ext_typ_obj
then
172 # An array starts with a *ext*, it must be a serialized object
174 # New object declaration
175 var id
= first
.data
.to_i
177 if cache
.has_id
(id
) then
179 errors
.add
new Error("Deserialization Error: object with id {id} is deserialized twice.")
187 if object
.length
>= 2 then
189 # Try to get the type name as a string
191 if o
isa String and static_type
== "String" and object
.length
== 2 then
195 var typ
= convert_object
(object
[i
], "String")
196 if typ
isa String then
203 if type_name
== null then
204 # There was no dynamic type
206 # We could use a `class_name_heuristic` here...
208 # Fallback to the static type
209 if static_type
!= null then
210 type_name
= static_type
.strip_nullable
213 if type_name
== null then
214 errors
.add
new Error("Deserialization Error: could not determine dynamic type of `{object}`.")
219 if not accept
(type_name
, static_type
) then return null
221 var attributes
= null
222 if object
.length
> i
then attributes
= object
[i
]
223 if not attributes
isa Map[nullable Serializable, nullable Serializable] then
224 # Some other type (could be an error), or there's no attributes
225 attributes
= new Map[nullable Serializable, nullable Serializable]
230 path_arrays
.push object
233 var value
= deserialize_class
(type_name
)
234 just_opened_id
= null
242 errors
.add
new Error("Deserialization Error: unknown MessagePack ext '{first.typ}'.")
246 # Plain array? Try to convert it to the desired static_type
247 if static_type
!= null then
248 return deserialize_class
(static_type
.strip_nullable
)
253 if object
isa Map[nullable Serializable, nullable Serializable] then
255 # TODO parse it as an instance of `static_type`
257 if static_type
!= null then
259 path_arrays
.push
null
261 just_opened_id
= null
262 var value
= deserialize_class
(static_type
.strip_nullable
)
273 if object
isa MsgPackExt then
275 # First try the custom extensions
276 var custom
= deserialize_ext
(object
, static_type
)
277 if custom
== null then
279 # No custom, go for deser standard references
280 if object
.typ
== ext_typ_ref
then
281 # Reference to an object
282 var id
= object
.data
.to_i
283 if not cache
.has_id
(id
) then
284 errors
.add
new Error("Deserialization Error: object reference id unknown.")
287 return cache
.object_for
(id
)
289 else if object
.typ
== ext_typ_char
then
291 return object
.data
.to_s
.first
293 else if object
.typ
== ext_typ_byte
then
295 return object
.data
.first
300 if object
isa String and object
.length
== 1 and static_type
== "Char" then
301 # Char serialized as a string
302 return object
.chars
.first
305 if object
isa Int and static_type
== "Byte" then
306 # Byte serialized as an integer
313 redef fun deserialize
(static_type
)
317 var root
= stream
.read_msgpack
318 return convert_object
(root
, static_type
)
321 # Hook to customize the deserialization of MessagePack extensions
323 # Redefine this method in subclasses to return custom Nit objects from
324 # an application specific extension.
326 # This method is invoked before dealing with the extensions used by the
327 # Nit serialization metadata [0x40..0x43]. In general, you should ignore
328 # them by returning `null`, but they can also be intercepted to comply to
329 # a format from a remote server.
330 protected fun deserialize_ext
(ext
: MsgPackExt, static_type
: nullable String): nullable Object
336 redef class SimpleCollection[E
]
337 redef init from_deserializer
(v
)
340 if v
isa MsgPackDeserializer then
341 v
.notify_of_creation
self
344 var open_array
= v
.path_arrays
.last
345 var msgpack_items
= null
346 if open_array
!= null then msgpack_items
= open_array
.last
348 if not msgpack_items
isa Array[nullable Serializable] then
349 v
.errors
.add
new Error("Deserialization Error: no items in source of `{class_name}`")
353 # Name of the dynamic name of E
354 var items_type_name
= (new GetName[E
]).to_s
357 for o
in msgpack_items
do
358 var obj
= v
.convert_object
(o
, items_type_name
)
361 else v
.errors
.add
new AttributeTypeError(self, "items", obj
, items_type_name
)
367 redef class Map[K
, V
]
368 redef init from_deserializer
(v
)
372 if v
isa MsgPackDeserializer then
373 v
.notify_of_creation
self
376 var open_object
= v
.path_arrays
.last
378 if open_object
!= null then
380 msgpack_items
= open_object
.last
382 msgpack_items
= v
.path
.last
385 if not msgpack_items
isa Map[nullable Object, nullable Object] then
386 v
.errors
.add
new Error("Deserialization Error: no key/values in source of `{class_name}`")
390 var keys_type_name
= (new GetName[K
]).to_s
391 var values_type_name
= (new GetName[V
]).to_s
393 for key_src
, value_src
in msgpack_items
do
394 var key
= v
.convert_object
(key_src
, keys_type_name
)
395 if not key
isa K
then
396 v
.errors
.add
new AttributeTypeError(self, "keys", key
, keys_type_name
)
400 var value
= v
.convert_object
(value_src
, values_type_name
)
401 if not value
isa V
then
402 v
.errors
.add
new AttributeTypeError(self, "values", value
, values_type_name
)