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 redef fun serialize
(object
)
30 if object
== null then
32 else object
.serialize_to_json
(self)
35 redef fun serialize_attribute
(name
, value
)
37 stream
.write
", \"{name}\
": "
41 redef fun serialize_reference
(object
)
43 if refs_map
.keys
.has
(object
) then
44 # if already serialized, add local reference
45 var id
= ref_id_for
(object
)
46 stream
.write
"\{\"__kind\": \"ref\", \"__id\": {id}\}"
53 # Map of references to already serialized objects
54 var refs_map
= new HashMap[Serializable,Int]
56 private fun ref_id_for
(object
: Serializable): Int
58 if refs_map
.keys
.has
(object
) then
59 return refs_map
[object
]
61 var id
= refs_map
.length
68 # Deserializer from a Json string
69 class JsonDeserializer
72 private var text
: Text
73 var root
: nullable Jsonable is noinit
74 var path
= new Array[JsonObject]
75 var id_to_object
= new HashMap[Int, Object]
77 var just_opened_id
: nullable Int = null
80 var root
= text
.parse_json
81 if root
isa JsonObject then path
.add
(root
)
85 redef fun deserialize_attribute
(name
)
87 assert not path
.is_empty
88 var current
= path
.last
90 assert current
.keys
.has
(name
)
91 var value
= current
[name
]
93 return convert_object
(value
)
96 # This may be called multiple times by the same object from constructors
97 # in different nclassdef
98 redef fun notify_of_creation
(new_object
)
100 var id
= just_opened_id
102 id_to_object
[id
] = new_object
105 # Convert from simple Json object to Nit object
106 private fun convert_object
(object
: nullable Object): nullable Object
108 if object
isa JsonObject then
109 assert object
.keys
.has
("__kind")
110 var kind
= object
["__kind"]
113 if kind
== "ref" then
114 assert object
.keys
.has
("__id")
115 var id
= object
["__id"]
118 assert id_to_object
.keys
.has
(id
)
119 return id_to_object
[id
]
123 if kind
== "obj" then
124 assert object
.keys
.has
("__id")
125 var id
= object
["__id"]
128 assert object
.keys
.has
("__class")
129 var class_name
= object
["__class"]
130 assert class_name
isa String
132 assert not id_to_object
.keys
.has
(id
) else print
"Error: Object with id '{id}' is deserialized twice."
138 var value
= deserialize_class
(class_name
)
139 just_opened_id
= null
148 if kind
== "char" then
149 assert object
.keys
.has
("__val")
150 var val
= object
["__val"]
151 assert val
isa String
153 if val
.length
!= 1 then print
"Error: expected a single char when deserializing '{val}'."
155 return val
.chars
.first
158 print
"Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
162 if object
isa Array[nullable Object] then
163 # special case, isa Array[nullable Serializable]
164 var array
= new Array[nullable Serializable]
165 for e
in object
do array
.add e
.as(nullable Serializable)
172 redef fun deserialize
do return convert_object
(root
)
175 redef class Serializable
176 private fun serialize_to_json
(v
: JsonSerializer)
178 var id
= v
.ref_id_for
(self)
179 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
186 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
190 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
194 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
198 redef fun serialize_to_json
(v
) do v
.stream
.write
"\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
202 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_json
)
205 redef class NativeString
206 redef fun serialize_to_json
(v
) do to_s
.serialize_to_json
(v
)
210 redef fun serialize_to_json
(v
)
212 if class_name
== "Array[nullable Serializable]" then
213 # Using class_name to the the exact type
214 # We do not want Array[Int] or anything else here
220 else v
.stream
.write
(", ")
222 if not v
.try_to_serialize
(e
) then
223 v
.warn
("element of type {e.class_name} is not serializable.")
228 # Register as pseudo object
229 var id
= v
.ref_id_for
(self)
230 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
231 v
.stream
.write
""", "__length": {{{length}}}, "__items": ["""
236 else v
.stream
.write
(", ")
238 if not v
.try_to_serialize
(e
) then
239 v
.warn
("element of type {e.class_name} is not serializable.")
247 init from_deserializer
(v
: Deserializer)
249 if v
isa JsonDeserializer then
250 v
.notify_of_creation
self
252 var length
= v
.deserialize_attribute
("__length").as(Int)
253 var arr
= v
.path
.last
["__items"].as(SequenceRead[nullable Object])
254 for i
in length
.times
do
255 var obj
= v
.convert_object
(arr
[i
])