Serialization

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:

- obtains a `SerializationContext` by calling `serialization()`,
- uses `SerializationContext.references()` to find the references that an instance holds,
- proceeds to traverse the graph of instances according to library-specific rules

## Deserializers

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

A deserializer provided by a serialization library:

- obtains a `DeserializationContext` by calling `deserialization()`

- progressively provides information about instances:

  - associates an instance with its class,
  - associates a member instance with its containing instance,
  - associates a referred instance with its referring instance and attribute,
  - associates a referred instance with its referring array index,
  - associates an instance with a given other instance. This is useful for instances which refer to singletons (on deserialization the singleton value has to be provided, since it cannot be instantiated by the serialization API).

- once the context has complete information about the instance and every instance reachable from it the instance can be reconstructed.

## 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
`uninitializedLateValue` | Source Code `shared 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
`deserialization` | Source Code `shared DeserializationContext<Id> deserialization<Id>() given Id satisfies Object` A new `DeserializationContext`.
`serialization` | Source Code `shared SerializationContext serialization()` A new `SerializationContext`.

Interfaces
`DeserializationContext` | Source Code `shared 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.
`Element` | Source Code `shared 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);
`Member` | Source Code `shared Member` An instance referring to another instance via a reference attribute.
`Outer` | Source Code `shared Outer` A member instance referring to its outer instance.
`ReachableReference` | Source Code `shared ReachableReference` A means via which one instance can refer to another.
`References` | Source Code `shared References` Exposes the instances directly reachable from a given instance.
`SerializationContext` | Source Code `shared 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
`UninitializedLateValue` | Source Code `shared abstract UninitializedLateValue` The type of `uninitializedLateValue`.
`uninitializedLateValue` | Source Code `shared 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
`DeserializationException` | Source Code `shared DeserializationException` Thrown to indicate an exception event during deserialization
`SerializationException` | Source Code `shared SerializationException` Thrown to indicate an exception event during serialization