1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Handles serialization and deserialization of objects to/from Json.
18 module json_serialization
23 # Serializer of Nit objects to Json string.
27 # Target writing stream
30 redef fun serialize
(object
)
32 if object
== null then
34 else object
.serialize_to_json
(self)
37 redef fun serialize_attribute
(name
, value
)
39 stream
.write
", \"{name}\
": "
43 redef fun serialize_reference
(object
)
45 if refs_map
.keys
.has
(object
) then
46 # if already serialized, add local reference
47 var id
= ref_id_for
(object
)
48 stream
.write
"\{\"__kind\": \"ref\", \"__id\": {id}\}"
55 # Map of references to already serialized objects.
56 var refs_map
= new HashMap[Serializable,Int]
58 # Get the internal serialized reference for this `object`.
59 private fun ref_id_for
(object
: Serializable): Int
61 if refs_map
.keys
.has
(object
) then
62 return refs_map
[object
]
64 var id
= refs_map
.length
71 # Deserializer from a Json string.
72 class JsonDeserializer
75 # Json text to deserialize from.
76 private var text
: Text
78 # Root json object parsed from input text.
79 var root
: nullable Jsonable is noinit
81 # Depth-first path in the serialized object tree.
82 var path
= new Array[JsonObject]
84 # Map of refenrences to already deserialized objects.
85 var id_to_object
= new HashMap[Int, Object]
87 # Last encountered object reference id.
90 var just_opened_id
: nullable Int = null
93 var root
= text
.parse_json
94 if root
isa JsonObject then path
.add
(root
)
98 redef fun deserialize_attribute
(name
)
100 assert not path
.is_empty
101 var current
= path
.last
103 assert current
.keys
.has
(name
)
104 var value
= current
[name
]
106 return convert_object
(value
)
109 # This may be called multiple times by the same object from constructors
110 # in different nclassdef
111 redef fun notify_of_creation
(new_object
)
113 var id
= just_opened_id
115 id_to_object
[id
] = new_object
118 # Convert from simple Json object to Nit object
119 private fun convert_object
(object
: nullable Object): nullable Object
121 if object
isa JsonObject then
122 assert object
.keys
.has
("__kind")
123 var kind
= object
["__kind"]
126 if kind
== "ref" then
127 assert object
.keys
.has
("__id")
128 var id
= object
["__id"]
131 assert id_to_object
.keys
.has
(id
)
132 return id_to_object
[id
]
136 if kind
== "obj" then
137 assert object
.keys
.has
("__id")
138 var id
= object
["__id"]
141 assert object
.keys
.has
("__class")
142 var class_name
= object
["__class"]
143 assert class_name
isa String
145 assert not id_to_object
.keys
.has
(id
) else print
"Error: Object with id '{id}' is deserialized twice."
151 var value
= deserialize_class
(class_name
)
152 just_opened_id
= null
161 if kind
== "char" then
162 assert object
.keys
.has
("__val")
163 var val
= object
["__val"]
164 assert val
isa String
166 if val
.length
!= 1 then print
"Error: expected a single char when deserializing '{val}'."
168 return val
.chars
.first
171 print
"Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
175 if object
isa Array[nullable Object] then
176 # special case, isa Array[nullable Serializable]
177 var array
= new Array[nullable Serializable]
178 for e
in object
do array
.add e
.as(nullable Serializable)
185 redef fun deserialize
do return convert_object
(root
)
188 redef class Serializable
189 private fun serialize_to_json
(v
: JsonSerializer)
191 var id
= v
.ref_id_for
(self)
192 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
199 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
203 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
207 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
211 redef fun serialize_to_json
(v
) do v
.stream
.write
"\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
215 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_json
)
218 redef class NativeString
219 redef fun serialize_to_json
(v
) do to_s
.serialize_to_json
(v
)
223 redef fun serialize_to_json
(v
)
225 if class_name
== "Array[nullable Serializable]" then
226 # Using class_name to the the exact type
227 # We do not want Array[Int] or anything else here
233 else v
.stream
.write
(", ")
235 if not v
.try_to_serialize
(e
) then
236 v
.warn
("element of type {e.class_name} is not serializable.")
241 # Register as pseudo object
242 var id
= v
.ref_id_for
(self)
243 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
244 v
.stream
.write
""", "__length": {{{length}}}, "__items": ["""
249 else v
.stream
.write
(", ")
251 if not v
.try_to_serialize
(e
) then
252 v
.warn
("element of type {e.class_name} is not serializable.")
260 # Instanciate a new `Array` from its serialized representation.
261 init from_deserializer
(v
: Deserializer)
263 if v
isa JsonDeserializer then
264 v
.notify_of_creation
self
266 var length
= v
.deserialize_attribute
("__length").as(Int)
267 var arr
= v
.path
.last
["__items"].as(SequenceRead[nullable Object])
268 for i
in length
.times
do
269 var obj
= v
.convert_object
(arr
[i
])