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
151 print
"Malformed Json string: unexpected Json Object kind '{kind}'"
158 redef fun deserialize
do return convert_object
(root
)
161 redef class Serializable
162 private fun serialize_to_json
(v
: JsonSerializer)
164 var id
= v
.ref_id_for
(self)
165 v
.stream
.write
"\{\"__kind\": \"obj\", \"__id\": {id}, \"__class\
": \"{class_name}\
""
172 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
176 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
180 redef fun serialize_to_json
(v
) do v
.stream
.write
(to_s
)
184 redef fun serialize_to_json
(v
) do v
.stream
.write
("'{to_s}'")
188 redef fun serialize_to_json
(v
) do v
.stream
.write
("\"{to_json_s}\
"")
190 private fun to_json_s
: String do return self.replace
("\\", "\\\\").
191 replace
("\"", "\\\
"").replace
("\b", "\\b").replace
("/", "\\/").
192 replace
("\n", "\\n").replace
("\r", "\\r").replace
("\t", "\\t")
193 # FIXME add support for unicode char when supported by Nit strings
194 # FIXME add support for \f! # .replace("\f", "\\f")
197 redef class NativeString
198 redef fun serialize_to_json
(v
) do to_s
.serialize_to_json
(v
)
202 redef fun serialize_to_json
(v
) do
208 else v
.stream
.write
(", ")
210 if not v
.try_to_serialize
(e
) then
211 v
.warn
("element of type {e.class_name} is not serializable.")