There are 3 main entrypoint services:
Writer::serialize_msgpack
adds an object to any stream writer.Serializable::serialize_msgpack
serializes the object to bytes.MsgPackSerializer
gives full control over the serialization of
Nit objets to the MessagePack format.Serializable
objects
msgpack :: serialization_write $ Bytes
A buffer containing Byte-manipulation facilitiesmsgpack :: serialization_write $ MsgPackExt
Application specific MessagePack extensionmsgpack :: serialization_write $ Serializable
Instances of this class can be passed toSerializer::serialize
msgpack :: serialization_write $ SimpleCollection
Items can be added to these collections.msgpack :: serialization_write $ Text
High-level abstraction for all text representationsSerializable
objects
msgpack :: serialization_write $ Bytes
A buffer containing Byte-manipulation facilitiesmsgpack :: serialization_write $ MsgPackExt
Application specific MessagePack extensionmsgpack :: serialization_write $ Serializable
Instances of this class can be passed toSerializer::serialize
msgpack :: serialization_write $ SimpleCollection
Items can be added to these collections.msgpack :: serialization_write $ Text
High-level abstraction for all text representationsSerializable::inspect
to show more useful information
serialization :: serialization_core
Abstract services to serialize Nit objects to different formatscore :: union_find
union–find algorithm using an efficient disjoint-set data structuremsgpack :: serialization_common
Serialization services forserialization_write
and serialization_read
# Serialize full Nit objects to MessagePack format
#
# There are 3 main entrypoint services:
# * `Writer::serialize_msgpack` adds an object to any stream writer.
# * `Serializable::serialize_msgpack` serializes the object to bytes.
# * `MsgPackSerializer` gives full control over the serialization of
# Nit objets to the MessagePack format.
module serialization_write
import serialization::caching
private import serialization::engine_tools
import serialization_common
private import write
import ext
# MessagePack deserialization engine
class MsgPackSerializer
super CachingSerializer
super MsgPackEngine
# Target writing stream
var stream: Writer
# Write plain MessagePack without metadata for deserialization?
#
# If `false`, the default, serialize to support deserialization:
#
# * Each object is encapsulated in an array that contains metadata and
# the actual object attributes in a map. The metadata includes the type
# name and references to already serialized object. This information
# supports deserializing the message, including cycles.
# * Preserve the Nit `Char` and `Byte` types as an object.
# * The generated MessagePack is standard and can be read by non-Nit programs.
# However, it contains some complexity that may make it harder to use.
#
# If `true`, serialize only the real data or non-Nit programs:
#
# * Nit objects are serialized to pure and standard MessagePack so they can
# be easily read by non-Nit programs.
# * Nit objects are serialized at every reference, so they may be duplicated.
# It is easier to read but it creates a larger output and it does not support
# cycles. Cyclic references are replaced by `null`.
# * The serialized data can only be deserialized to their expected static
# types, losing the knowledge of their dynamic type.
var plain_msgpack = false is writable
# Should strings declaring the objects type and attributes name be cached?
#
# If `true` metadata strings are cached using `cache`.
# The first occurrence is written as an object declaration,
# successive occurrences are written as an object reference.
#
# If `false`, the default, metadata strings are written as pure MessagePack
# strings, without their own metadata.
#
# Using the cache may save some space by avoiding the repetition of
# names used by many types or attributes.
# However, it adds complexity to the generated message and may be less
# safe for versioning.
var cache_metadata_strings = false is writable
# List of the current open objects, the first is the main target of the serialization
#
# Used only when `plain_msgpack == true` to detect cycles in serialization.
private var open_objects = new Array[Object]
redef var current_object = null
redef fun serialize(object)
do
if object == null then
stream.write_msgpack_null
else
if plain_msgpack then
for o in open_objects do
if object.is_same_serialized(o) then
# Cycle, can't be managed in plain_msgpack mode
warn "Cycle detected in serialized object, replacing reference with 'null'."
stream.write_msgpack_null
return
end
end
open_objects.add object
end
var last_object = current_object
current_object = object
object.accept_msgpack_serializer self
current_object = last_object
if plain_msgpack then open_objects.pop
end
end
redef fun serialize_attribute(name, value)
do
serialize_meta_string name
super
end
redef fun serialize_reference(object)
do
if not plain_msgpack and cache.has_object(object) then
# if already serialized, add local reference
var id = cache.id_for(object)
stream.write_msgpack_ext(ext_typ_ref, id.to_bytes)
else
# serialize
serialize object
end
end
private fun serialize_meta_string(type_name: String)
do
if plain_msgpack or not cache_metadata_strings then
# String only version
stream.write_msgpack_str type_name
return
end
if cache.has_object(type_name) then
# if already serialized, add reference
var id = cache.id_for(type_name)
stream.write_msgpack_ext(ext_typ_ref, id.to_bytes)
else
# serialize
var id = cache.new_id_for(type_name)
stream.write_msgpack_array 2 # obj+id, type_name
stream.write_msgpack_ext(ext_typ_obj, id.to_bytes)
stream.write_msgpack_str type_name
end
end
end
# Serialization visitor to count attribute in `Serializable` objects
class AttributeCounter
super Serializer
# Number of attributes counted
var count = 0
redef fun serialize_attribute(name, object) do count += 1
end
# ---
# Services and serializables
redef class Writer
# Serialize `value` in MessagePack format
fun serialize_msgpack(value: nullable Serializable, plain: nullable Bool)
do
var serializer = new MsgPackSerializer(self)
serializer.plain_msgpack = plain or else false
serializer.serialize value
end
end
redef class Serializable
# Serialize `self` to MessagePack bytes
#
# Set `plain = true` to generate standard MessagePack, without deserialization metadata.
# Use this option if the generated MessagePack will be read by non-Nit programs.
# Use the default, `plain = false`, if the MessagePack bytes are to be deserialized by a Nit program.
fun serialize_msgpack(plain: nullable Bool): Bytes
do
var stream = new BytesWriter
stream.serialize_msgpack(self, plain)
stream.close
return stream.bytes
end
# Hook to customize the serialization of this class to MessagePack
#
# 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 `MsgPackSerializer`.
#
# Most of the time, it is better to refine the method `core_serialize_to`
# which is used by all the serialization engines, not just MessagePack.
protected fun accept_msgpack_serializer(v: MsgPackSerializer)
do
# Count the number of attributes
var attribute_counter = new AttributeCounter
accept_msgpack_attribute_counter attribute_counter
var n_attributes = attribute_counter.count
if not v.plain_msgpack then
var n_meta_items = 2
if n_attributes > 0 then n_meta_items += 1
n_meta_items += msgpack_extra_array_items # obj+id, class_name, attributes
# Metadata
var id = v.cache.new_id_for(self)
v.stream.write_msgpack_array n_meta_items
v.stream.write_msgpack_ext(v.ext_typ_obj, id.to_bytes)
v.serialize_meta_string class_name
if n_attributes > 0 then v.stream.write_msgpack_map n_attributes
else
v.stream.write_msgpack_map n_attributes
end
v.serialize_core self
end
# Hook to customize the behavior of the `AttributeCounter`
#
# By default, this method makes `v` visits all serializable attributes.
protected fun accept_msgpack_attribute_counter(v: AttributeCounter)
do
v.serialize_core self
end
# Hook to request a larger than usual metadata array
#
# Use by `SimpleCollection` and `Map` to append the items after
# the metadata and attributes.
protected fun msgpack_extra_array_items: Int do return 0
end
redef class MsgPackExt
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_ext(typ, data)
end
redef class Text
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_str self
end
redef class Int
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_int self
end
redef class Float
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_double self
end
redef class Bool
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_bool self
end
redef class Byte
redef fun accept_msgpack_serializer(v)
do
if v.plain_msgpack then
# Write as a string
v.stream.write_msgpack_int to_i
else
# Write as ext
var bytes = new Bytes.with_capacity(1)
bytes.add self.to_i
v.stream.write_msgpack_ext(v.ext_typ_byte, bytes)
end
end
end
redef class Char
redef fun accept_msgpack_serializer(v)
do
if v.plain_msgpack then
# Write as a string
v.stream.write_msgpack_fixstr to_s
else
# Write as ext
var bytes = to_s.to_bytes
v.stream.write_msgpack_ext(v.ext_typ_char, bytes)
end
end
end
redef class Bytes
redef fun accept_msgpack_serializer(v) do v.stream.write_msgpack_bin self
end
redef class CString
redef fun accept_msgpack_serializer(v) do to_s.accept_msgpack_serializer(v)
end
redef class SimpleCollection[E]
redef fun accept_msgpack_serializer(v)
do
if not v.plain_msgpack then
# Add metadata and other attributes
super
end
# Header
v.stream.write_msgpack_array length
# Items
for e in self do
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_msgpack_null
end
end
end
redef fun msgpack_extra_array_items do return 1
end
redef class Map[K, V]
redef fun accept_msgpack_serializer(v)
do
if not v.plain_msgpack then
# Add metadata and other attributes
super
end
# Header
v.stream.write_msgpack_map keys.length
# Key / values, alternating
for key, val in self do
if not v.try_to_serialize(key) then
assert val != null # null would have been serialized
v.warn "element of type {val.class_name} is not serializable."
v.stream.write_msgpack_null
end
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_msgpack_null
end
end
end
redef fun msgpack_extra_array_items do return 1
end
lib/msgpack/serialization_write.nit:15,1--349,3