The Ceylon serialization infrastructure.

This package provides an API for serialization libraries to provide their own serialization APIs to client code. It does not provide a serialization service itself.

This API:

  • provides a way to walk the graph of instances added for serialization, including those reachable via non-`shared` attributes,
  • copes with shared references and cyclic references in object graphs,
  • prevents access to uninitialized and partially initialized instances during deserialization.
    (this includes instances reachable from other instances, transitively).

The API cannot prevent serialization libraries from obtaining access to non-`shared` state of instances (loss of encapsulation is an inevitable part of serialization).

A serialization library will implement some kind of serializer and/or deserializer (frequently both) using the facilities of this API.

Serializers

A serializer takes one or many Ceylon instances and serialize them (and the instances reachable from them) to some external representation, usually bytes or text in some specific format.

A serializer provided by a serialization library:

Deserializers

A deserializer constructs a graph of Ceylon instances according to some external representation.

A deserializer provided by a serialization library:

Serializability

Serializability is whether or not a particular serialization library can serialize a particular instance.

Instances of classes which are annotated serializable() are able to enumerate their state during serialization, and initialize their state during deserialization. However, that does not imply that every serialization library can necessarily serialize instances of every serializable class.

The serializability of an instance can depend on:

  • the class of the instance, and its super classes,
  • the serializability of the outer instance(s), if the object is an instance of a member class. ,
  • the serialization library's support for serializing generic classes, and member classes,
  • the serialization library's support for serializing anonymous objects (such as true, null or smaller),
  • the underlying serialization format the serialization library supports

The deserializability of an instance can depend on:

  • The runtime availability and compatibility of the class of the instances, and its super classes, for each instance in the object graph.
By: Gavin, Tom
Since 1.2.0
Values
uninitializedLateValueSource Codeshared uninitializedLateValue uninitializedLateValue

A singleton used to indicate that a late Member of a particular instance has not been initialized.

For example, given

class Example() {
    shared late Example parent;
}

Then

value ex = Example();// uninitialized parent
value context = serialization();
value refs = context.references(ex);
assert(is Member parentRef = refs.find((element) => element is Member));
assert(parentRef.referred(ex) == uninitializedLateValue);

Thus, if a serialization library supports it, it is possible to serialize uninitialized late values.

Functions
deserializationSource Codeshared DeserializationContext<Id> deserialization<Id>()
given Id satisfies Object
serializationSource Codeshared SerializationContext serialization()
Interfaces
DeserializationContextSource Codeshared DeserializationContext<Id>

A contract for identifying instances, specifying their classes, attributes, elements and values, and ultimately reconstructing those instances.

Instances are identified using the Id's semantics for equality. The methods of this interface can be called in any order; the id serves to associate each method invocation with the instance(s) to pertains to. The only constraint is that DeserializationContext.reconstruct() will throw if the context lacks enough information to fully initialize the requested instance or any instance reachable from it. Reference cycles are supported.

For example, given

serializable class Person(name, employer) {
    shared String name;
    shared Company employer;
}
serializable class Company(name) {
    shared String name;
    shared late Person owner;
}

And an instance graph corresponding to:

value wonkaInc = Company("Wonka Inc.");
value willy = Person("Willy Wonka", wonkaInc);
value umpaLumpa = Person("Umpa lumpa", wonkaInc);
wonkaInc.owner = willy;

Then we could reconstruct that instance graph like so:

value dc = deserialization<String>();

dc.attribute("ww", `value Person.name`, "wwn");
dc.attribute("ww", `value Person.employer`, "wi");
dc.attribute("ul", `value Person.name`, "uln");
dc.attribute("ul", `value Person.employer`, "wi");
dc.attribute("wi", `value Company.name`, "win");
dc.attribute("wi", `value Company.owner`, "ww");

dc.instanceValue("win", "Wonka Inc.");
dc.instanceValue("wwn", "Willy Wonka");
dc.instanceValue("uln", "Umpa lumpa");

dc.instance("wi", `Company`);
dc.instance("ww", `Person`);
dc.instance("ul", `Person`);

value wonkaInc2 = dc.reconstruct<Company>("wi");
value willy2 = dc.reconstruct<Person>("ww");
value umpaLumpa2 = dc.reconstruct<Person>("ul");

assert(wonkaInc2.owner === willy2);
assert(willy2.employer === wonkaInc2);
assert(umpaLumpa2.employer === wonkaInc2);

The calls to attribute, DeserializationContext.instanceValue() and DeserializationContext.instance() could be in any order.

ElementSource Codeshared Element

An Array instance referring to another instance via one of its elements.

For example, given:

value arr = Array({"hello"});
value context = serialization();
value refs = context.references(arr);
assert(is Element elementRef = refs.find((element) => element is Element));
assert(elementRef.referred(arr) == "hello");
assert(elementRef.index == 0);
MemberSource Codeshared Member

An instance referring to another instance via a reference attribute.

OuterSource Codeshared Outer

A member instance referring to its outer instance.

ReachableReferenceSource Codeshared ReachableReference

A means via which one instance can refer to another.

ReferencesSource Codeshared References

Exposes the instances directly reachable from a given instance.

SerializationContextSource Codeshared SerializationContext

A context representing serialization of many objects to a single output stream.

The serialization library obtains an instance by calling serialization() and then uses SerializationContext.references() to traverse the instances reachable from the instance(s) being serialized.

It is the serialization library's responsibility to manage object identity and handle cycles in the graph of object references. For example a serialization library that produced a hierarchical format might ignore identity when an instance is encountered multiple times (resulting in duplicate subtrees in the output), and simply throw an exception if it encountered a cycle.

Classes
UninitializedLateValueSource Codeshared abstract UninitializedLateValue
uninitializedLateValueSource Codeshared uninitializedLateValue

A singleton used to indicate that a late Member of a particular instance has not been initialized.

For example, given

class Example() {
    shared late Example parent;
}

Then

value ex = Example();// uninitialized parent
value context = serialization();
value refs = context.references(ex);
assert(is Member parentRef = refs.find((element) => element is Member));
assert(parentRef.referred(ex) == uninitializedLateValue);

Thus, if a serialization library supports it, it is possible to serialize uninitialized late values.

Exceptions
DeserializationExceptionSource Codeshared DeserializationException

Thrown to indicate an exception event during deserialization

SerializationExceptionSource Codeshared SerializationException

Thrown to indicate an exception event during serialization