serialize_to_json
and JsonSerializer
json :: serialization_write $ Collection
The root of the collection hierarchy.json :: serialization_write $ Serializable
Instances of this class can be passed toSerializer::serialize
json :: serialization_write $ SimpleCollection
Items can be added to these collections.json :: serialization_write $ Text
High-level abstraction for all text representationsjson :: serialization_write $ Collection
The root of the collection hierarchy.json :: serialization_write $ Serializable
Instances of this class can be passed toSerializer::serialize
json :: serialization_write $ SimpleCollection
Items can be added to these collections.json :: serialization_write $ Text
High-level abstraction for all text representationsserialization :: serialization_core
Abstract services to serialize Nit objects to different formatscore :: union_find
union–find algorithm using an efficient disjoint-set data structureserialization :: custom_serialization
Example of an ad hoc serializer that is tailored to transform business specific objects into customized representation.app::http_request
main service AsyncHttpRequest
mpi :: mpi_simple
app.nit
on Android using a custom Java entry point
restful
annotation documented at lib/nitcorn/restful.nit
# Services to write Nit objects to JSON strings: `serialize_to_json` and `JsonSerializer`
module serialization_write
import ::serialization::caching
private import ::serialization::engine_tools
# Serializer of Nit objects to Json string.
class JsonSerializer
super CachingSerializer
# Target writing stream
var stream: Writer
# Write plain JSON? Standard JSON without metadata for deserialization
#
# If `false`, the default, serialize to support deserialization:
#
# * Write metadata, including the types of the serialized objects so they can
# be deserialized to their original form using `JsonDeserializer`.
# * Use references when an object has already been serialized so to not duplicate it.
# * Support cycles in references.
# * Preserve the Nit `Char` and `Byte` types as special objects.
# * The generated JSON is standard and can be read by non-Nit programs.
# However, some Nit types are not represented by the simplest possible JSON representation.
# With the added metadata, it can be complex to read.
#
# If `true`, serialize for other programs:
#
# * Nit objects are serialized to pure and standard JSON so they can
# be easily read by non-Nit programs and humans.
# * Nit objects are serialized for every references, so they can be duplicated.
# It is easier to read but it creates a larger output.
# * Does not support cycles, will replace the problematic references by `null`.
# * Does not serialize the metadata needed to deserialize the objects
# back to regular Nit objects.
# * Keys of Nit `HashMap` are converted to their string representation using `to_s`.
var plain_json = false is writable
# Write pretty JSON for human eyes?
#
# Toggles skipping lines between attributes of an object and
# properly indent the written JSON.
var pretty_json = false is writable
# Current indentation level used for writing `pretty_json`
private var indent_level = 0
# List of the current open objects, the first is the main target of the serialization
#
# Used only when `plain_json == true` to detect cycles in serialization.
private var open_objects = new Array[Object]
# Has the first attribute of the current object already been serialized?
#
# Used only when `plain_json == true`.
private var first_attribute = false
redef var current_object = null
redef fun serialize(object)
do
if object == null then
stream.write "null"
else
if plain_json then
for o in open_objects do
if object.is_same_serialized(o) then
# Cycle, can't be managed in plain json
warn "Cycle detected in serialized object, replacing reference with 'null'."
stream.write "null"
return
end
end
open_objects.add object
end
first_attribute = true
var last_object = current_object
current_object = object
object.accept_json_serializer self
first_attribute = false
current_object = last_object
if plain_json then open_objects.pop
end
end
redef fun serialize_attribute(name, value)
do
if not plain_json or not first_attribute then
stream.write ","
end
first_attribute = false
new_line_and_indent
stream.write "\""
stream.write name
stream.write "\":"
if pretty_json then stream.write " "
super
end
redef fun serialize_reference(object)
do
if not plain_json and cache.has_object(object) then
# if already serialized, add local reference
var id = cache.id_for(object)
stream.write "\{"
indent_level += 1
new_line_and_indent
stream.write "\"__kind\": \"ref\", \"__id\": "
stream.write id.to_s
indent_level -= 1
new_line_and_indent
stream.write "\}"
else
# serialize here
serialize object
end
end
# Write a new line and indent it, only if `pretty_json`
private fun new_line_and_indent
do
if pretty_json then
stream.write "\n"
for i in indent_level.times do stream.write "\t"
end
end
end
redef class Text
redef fun accept_json_serializer(v)
do
v.stream.write "\""
var start_i = 0
var escaped = null
for i in [0 .. self.length[ do
var char = self[i]
if char == '\\' then
escaped = "\\\\"
else if char == '\"' then
escaped = "\\\""
else if char < ' ' then
if char == '\n' then
escaped = "\\n"
else if char == '\r' then
escaped = "\\r"
else if char == '\t' then
escaped = "\\t"
else
escaped = char.escape_to_utf16
end
end
if escaped != null then
# Write open non-escaped string
if start_i <= i then
v.stream.write substring(start_i, i-start_i)
end
# Write escaped character
v.stream.write escaped
escaped = null
start_i = i+1
end
end
# Write remaining non-escaped string
if start_i < length then
if start_i == 0 then
v.stream.write self
else
v.stream.write substring(start_i, length-start_i)
end
end
v.stream.write "\""
end
end
redef class Serializable
# Serialize `self` to JSON
#
# Set `plain = true` to generate standard JSON, without deserialization metadata.
# Use this option if the generated JSON will be read by other programs or humans.
# Use the default, `plain = false`, if the JSON is to be deserialized by a Nit program.
#
# Set `pretty = true` to generate pretty JSON for human eyes.
# Use the default, `pretty = false`, to generate minified JSON.
#
# This method should not be refined by subclasses,
# instead `accept_json_serializer` can customize the serialization of an object.
#
# See: `JsonSerializer`
fun serialize_to_json(plain, pretty: nullable Bool): String
do
var stream = new StringWriter
var serializer = new JsonSerializer(stream)
serializer.plain_json = plain or else false
serializer.pretty_json = pretty or else false
serializer.serialize self
stream.close
return stream.to_s
end
# Serialize `self` to plain JSON
#
# Compatibility alias for `serialize_to_json(plain=true)`.
fun to_json: String do return serialize_to_json(plain=true)
# Serialize `self` to plain pretty JSON
#
# Compatibility alias for `serialize_to_json(plain=true, pretty=true)`.
fun to_pretty_json: String do return serialize_to_json(plain=true, pretty=true)
# Refinable service to customize the serialization of this class to JSON
#
# This method can be refined to customize the serialization by either
# writing pure JSON directly on the stream `v.stream` or
# by using other services of `JsonSerializer`.
#
# Most of the time, it is preferable to refine the method `core_serialize_to`
# which is used by all the serialization engines, not just JSON.
protected fun accept_json_serializer(v: JsonSerializer)
do
v.stream.write "\{"
v.indent_level += 1
if not v.plain_json then
var id = v.cache.new_id_for(self)
v.new_line_and_indent
v.stream.write "\"__kind\": \"obj\", \"__id\": "
v.stream.write id.to_s
v.stream.write ", \"__class\": \""
v.stream.write class_name
v.stream.write "\""
end
v.serialize_core(self)
v.indent_level -= 1
v.new_line_and_indent
v.stream.write "\}"
end
end
redef class Int
redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Float
redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Bool
redef fun accept_json_serializer(v) do v.stream.write to_s
end
redef class Char
redef fun accept_json_serializer(v)
do
if v.plain_json then
to_s.accept_json_serializer v
else
v.stream.write "\{\"__kind\": \"char\", \"__val\": "
to_s.accept_json_serializer v
v.stream.write "\}"
end
end
end
redef class Byte
redef fun accept_json_serializer(v)
do
if v.plain_json then
to_i.accept_json_serializer v
else
v.stream.write "\{\"__kind\": \"byte\", \"__val\": "
to_i.accept_json_serializer v
v.stream.write "\}"
end
end
end
redef class CString
redef fun accept_json_serializer(v) do to_s.accept_json_serializer(v)
end
redef class Collection[E]
# Utility to serialize a normal Json array
private fun serialize_to_pure_json(v: JsonSerializer)
do
v.stream.write "["
var is_first = true
for e in self do
if is_first then
is_first = false
else
v.stream.write ","
if v.pretty_json then v.stream.write " "
end
if not v.try_to_serialize(e) then
assert e != null # null would have been serialized
v.warn("element of type {e.class_name} is not serializable.")
v.stream.write "null"
end
end
v.stream.write "]"
end
end
redef class SimpleCollection[E]
redef fun accept_json_serializer(v)
do
if v.plain_json then
serialize_to_pure_json v
else
# Register as pseudo object
var id = v.cache.new_id_for(self)
v.stream.write """{"""
v.indent_level += 1
v.new_line_and_indent
v.stream.write """"__kind": "obj", "__id": """
v.stream.write id.to_s
v.stream.write """, "__class": """"
v.stream.write class_name
v.stream.write """","""
v.new_line_and_indent
v.stream.write """"__items": """
serialize_to_pure_json v
core_serialize_to v
v.indent_level -= 1
v.new_line_and_indent
v.stream.write "\}"
end
end
end
redef class Map[K, V]
redef fun accept_json_serializer(v)
do
# Register as pseudo object
var id = v.cache.new_id_for(self)
v.stream.write "\{"
v.indent_level += 1
if v.plain_json then
var first = true
for key, val in self do
if not first then
v.stream.write ","
else first = false
v.new_line_and_indent
var k = key or else "null"
k.to_s.accept_json_serializer v
v.stream.write ":"
if v.pretty_json then v.stream.write " "
if not v.try_to_serialize(val) then
assert val != null # null would have been serialized
v.warn("element of type {val.class_name} is not serializable.")
v.stream.write "null"
end
end
else
v.new_line_and_indent
v.stream.write """"__kind": "obj", "__id": """
v.stream.write id.to_s
v.stream.write """, "__class": """"
v.stream.write class_name
v.stream.write """", "__length": """
v.stream.write length.to_s
v.stream.write ","
v.new_line_and_indent
v.stream.write """"__keys": """
keys.serialize_to_pure_json v
v.stream.write ","
v.new_line_and_indent
v.stream.write """"__values": """
values.serialize_to_pure_json v
core_serialize_to v
end
v.indent_level -= 1
v.new_line_and_indent
v.stream.write "\}"
end
end
lib/json/serialization_write.nit:15,1--413,3