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
20 import simple_json_reader
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}'"
167 redef fun deserialize
do return convert_object
(root
)
170 redef class Serializable
171 private fun serialize_to_json
(v
: JsonSerializer)
173 var id
= v
.ref_id_for
(self)
174 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
181 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
185 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
189 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
193 redef fun serialize_to_json
(v
) do v
.stream
.write
"\{\"__kind\": \"char\", \"__val\": \"{to_s.to_json_s}\"\
}"
197 redef fun serialize_to_json(v) do v.stream.write("\
"{to_json_s}\"")
199 private fun to_json_s: String do return self.replace("\\
", "\\\\
").
200 replace("\
"", "\\\"").replace("/", "\\
/").
201 replace("\n
", "\\n
").replace("\r
", "\\r
").replace("\t
", "\\t
")
202 # FIXME add support for unicode char when supported by Nit strings
203 # FIXME add support for \f! # .replace("\f
", "\\f
")
204 # FIXME add support for \b .replace("\b
", "\\b
")
207 redef class NativeString
208 redef fun serialize_to_json(v) do to_s.serialize_to_json(v)
212 redef fun serialize_to_json(v) do
218 else v.stream.write(", ")
220 if not v.try_to_serialize(e) then
221 v.warn("element of
type {e.class_name} is not serializable
.")