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 module json_serialization
25 # Target writing stream
28 init(stream
: OStream) do self.stream
= 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 private fun ref_id_for
(object
: Serializable): Int
60 if refs_map
.keys
.has
(object
) then
61 return refs_map
[object
]
63 var id
= refs_map
.length
70 # Deserializer from a Json string
71 class JsonDeserializer
74 var root
: nullable Object
75 var path
= new Array[HashMap[String, nullable Object]]
76 var id_to_object
= new HashMap[Int, Object]
78 var just_opened_id
: nullable Int = null
82 var root
= text
.json_to_nit_object
83 if root
isa HashMap[String, nullable Object] then path
.add
(root
)
87 redef fun deserialize_attribute
(name
)
89 assert not path
.is_empty
90 var current
= path
.last
92 assert current
.keys
.has
(name
)
93 var value
= current
[name
]
95 return convert_object
(value
)
98 # This may be called multiple times by the same object from constructors
99 # in different nclassdef
100 redef fun notify_of_creation
(new_object
)
102 var id
= just_opened_id
104 id_to_object
[id
] = new_object
107 # Convert from simple Json object to Nit object
108 private fun convert_object
(object
: nullable Object): nullable Object
110 if object
isa HashMap[String, nullable Object] then
111 assert object
.keys
.has
("__kind")
112 var kind
= object
["__kind"]
115 if kind
== "ref" then
116 assert object
.keys
.has
("__id")
117 var id
= object
["__id"]
120 assert id_to_object
.keys
.has
(id
)
121 return id_to_object
[id
]
125 if kind
== "obj" then
126 assert object
.keys
.has
("__id")
127 var id
= object
["__id"]
130 assert object
.keys
.has
("__class")
131 var class_name
= object
["__class"]
132 assert class_name
isa String
134 assert not id_to_object
.keys
.has
(id
) else print
"Error: Object with id '{id}' is deserialized twice."
140 var value
= deserialize_class
(class_name
)
141 just_opened_id
= null
150 if kind
== "char" then
151 assert object
.keys
.has
("__val")
152 var val
= object
["__val"]
153 assert val
isa String
155 if val
.length
!= 1 then print
"Error: expected a single char when deserializing '{val}'."
157 return val
.chars
.first
160 print
"Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
164 if object
isa Array[nullable Object] then
165 # special case, isa Array[nullable Serializable]
166 var array
= new Array[nullable Serializable]
167 for e
in object
do array
.add e
.as(nullable Serializable)
174 redef fun deserialize
do return convert_object
(root
)
177 redef class Serializable
178 private fun serialize_to_json
(v
: JsonSerializer)
180 var id
= v
.ref_id_for
(self)
181 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
188 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
192 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
196 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
200 redef fun serialize_to_json
(v
) do v
.stream
.write
"\{\"__kind\": \"char\", \"__val\": \"{to_s.to_json_s}\"\
}"
204 redef fun serialize_to_json(v) do v.stream.write("\
"{to_json_s}\"")
206 private fun to_json_s: String do return self.replace("\\
", "\\\\
").
207 replace("\
"", "\\\"").replace("/", "\\
/").
208 replace("\n
", "\\n
").replace("\r
", "\\r
").replace("\t
", "\\t
")
209 # FIXME add support for unicode char when supported by Nit strings
210 # FIXME add support for \f! # .replace("\f
", "\\f
")
211 # FIXME add support for \b .replace("\b
", "\\b
")
214 redef class NativeString
215 redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
219 redef fun serialize_to_json(v)
221 if class_name == "Array[nullable Serializable]" then
222 # Using class_name to the the exact type
223 # We do not want Array[Int] or anything else here
229 else v.stream.write(", ")
231 if not v.try_to_serialize(e) then
232 v.warn("element of
type {e.class_name} is not serializable
.")
237 # Register as pseudo object
238 var id = v.ref_id_for(self)
239 v.stream.write "\
{\"__kind\": \"obj\", \"__id\": {id}, \
"__class\": \
"{class_name}\""
240 v.stream.write """, "__length
": {{{length}}}, "__items
": ["""
245 else v.stream.write(", ")
247 if not v.try_to_serialize(e) then
248 v.warn("element of
type {e.class_name} is not serializable
.")
256 init from_deserializer(v: Deserializer)
258 if v isa JsonDeserializer then
259 v.notify_of_creation self
261 var length = v.deserialize_attribute("__length
").as(Int)
262 var arr = v.path.last["__items
"].as(Array[nullable Object])
263 for i in length.times do
264 var obj = v.convert_object(arr[i])