lib/json_serialization: fixes old inits
[nit.git] / lib / json_serialization.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 module json_serialization
18
19 import serialization
20 import json::static
21
22 class JsonSerializer
23 super Serializer
24
25 # Target writing stream
26 var stream: OStream
27
28 redef fun serialize(object)
29 do
30 if object == null then
31 stream.write "null"
32 else object.serialize_to_json(self)
33 end
34
35 redef fun serialize_attribute(name, value)
36 do
37 stream.write ", \"{name}\": "
38 super
39 end
40
41 redef fun serialize_reference(object)
42 do
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}\}"
47 else
48 # serialize here
49 serialize object
50 end
51 end
52
53 # Map of references to already serialized objects
54 var refs_map = new HashMap[Serializable,Int]
55
56 private fun ref_id_for(object: Serializable): Int
57 do
58 if refs_map.keys.has(object) then
59 return refs_map[object]
60 else
61 var id = refs_map.length
62 refs_map[object] = id
63 return id
64 end
65 end
66 end
67
68 # Deserializer from a Json string
69 class JsonDeserializer
70 super Deserializer
71
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]
76
77 var just_opened_id: nullable Int = null
78
79 init do
80 var root = text.parse_json
81 if root isa JsonObject then path.add(root)
82 self.root = root
83 end
84
85 redef fun deserialize_attribute(name)
86 do
87 assert not path.is_empty
88 var current = path.last
89
90 assert current.keys.has(name)
91 var value = current[name]
92
93 return convert_object(value)
94 end
95
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)
99 do
100 var id = just_opened_id
101 assert id != null
102 id_to_object[id] = new_object
103 end
104
105 # Convert from simple Json object to Nit object
106 private fun convert_object(object: nullable Object): nullable Object
107 do
108 if object isa JsonObject then
109 assert object.keys.has("__kind")
110 var kind = object["__kind"]
111
112 # ref?
113 if kind == "ref" then
114 assert object.keys.has("__id")
115 var id = object["__id"]
116 assert id isa Int
117
118 assert id_to_object.keys.has(id)
119 return id_to_object[id]
120 end
121
122 # obj?
123 if kind == "obj" then
124 assert object.keys.has("__id")
125 var id = object["__id"]
126 assert id isa Int
127
128 assert object.keys.has("__class")
129 var class_name = object["__class"]
130 assert class_name isa String
131
132 assert not id_to_object.keys.has(id) else print "Error: Object with id '{id}' is deserialized twice."
133
134 # advance on path
135 path.push object
136
137 just_opened_id = id
138 var value = deserialize_class(class_name)
139 just_opened_id = null
140
141 # revert on path
142 path.pop
143
144 return value
145 end
146
147 # char?
148 if kind == "char" then
149 assert object.keys.has("__val")
150 var val = object["__val"]
151 assert val isa String
152
153 if val.length != 1 then print "Error: expected a single char when deserializing '{val}'."
154
155 return val.chars.first
156 end
157
158 print "Malformed Json string: unexpected Json Object kind '{kind or else "null"}'"
159 abort
160 end
161
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)
166 return array
167 end
168
169 return object
170 end
171
172 redef fun deserialize do return convert_object(root)
173 end
174
175 redef class Serializable
176 private fun serialize_to_json(v: JsonSerializer)
177 do
178 var id = v.ref_id_for(self)
179 v.stream.write "\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\": \"{class_name}\""
180 core_serialize_to(v)
181 v.stream.write "\}"
182 end
183 end
184
185 redef class Int
186 redef fun serialize_to_json(v) do v.stream.write(to_s)
187 end
188
189 redef class Float
190 redef fun serialize_to_json(v) do v.stream.write(to_s)
191 end
192
193 redef class Bool
194 redef fun serialize_to_json(v) do v.stream.write(to_s)
195 end
196
197 redef class Char
198 redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}"
199 end
200
201 redef class String
202 redef fun serialize_to_json(v) do v.stream.write(to_json)
203 end
204
205 redef class NativeString
206 redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
207 end
208
209 redef class Array[E]
210 redef fun serialize_to_json(v)
211 do
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
215 v.stream.write "["
216 var is_first = true
217 for e in self do
218 if is_first then
219 is_first = false
220 else v.stream.write(", ")
221
222 if not v.try_to_serialize(e) then
223 v.warn("element of type {e.class_name} is not serializable.")
224 end
225 end
226 v.stream.write "]"
227 else
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": ["""
232 var is_first = true
233 for e in self do
234 if is_first then
235 is_first = false
236 else v.stream.write(", ")
237
238 if not v.try_to_serialize(e) then
239 v.warn("element of type {e.class_name} is not serializable.")
240 end
241 end
242 v.stream.write "]"
243 v.stream.write "\}"
244 end
245 end
246
247 init from_deserializer(v: Deserializer)
248 do
249 if v isa JsonDeserializer then
250 v.notify_of_creation self
251
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])
256 self.add obj
257 end
258 end
259 end
260 end