- SL = serialization library.
- SC =
SerializationContext
- DC =
DeserializationContext
Add method SerializationContext.getReference<Instance>(Instance instance)
to get the SerializableReference
of an already registered instance:
- Done, but the definition of
==
forInteger
andFloat
means we can't use those as instances, which seems wrong.
Done: Review tabular having done this: It should be simpler
SerializableReference.serialize()
is the problem here. It originally returned a Deconstructed
, which fits the recursive case quite well, because then the SL has the job of picking over/iterating the values in the Deconstructed
and emitting them to the format (e.g. copying them into a JSON Object, or whatever).
But I changed the API because:
- It seemed suboptimal to me that we would (effectively) have to instantiate a few maps for every instance we needed to persist, just to buffer the state of the instance. Far better, I thought, for instance to push its state into a Deconstructed provided by the SL than the SL have to pull it out of something that has to be allocated fresh each time.
- But doing that made the API more SAX-like, and really the SL needed notification of each
ClassDeclaration
we encountered on the way up the inheritance hierarchy. Hence the value parameter became a function parameter.
Fix the serialization of member classes
How we handle generics is fucked up: tabular
uses Deconstructor.putTypeArgument()
, but not Deconstructed.getTypeArgument()
. Instead it looks at the type of the argument as it was persisted in order to figure out the ClassModel
it needs to instantiate. Having instantiated the right thing there's no need for the SL to call Deconstructed.getTypeArgument()
when setting the state.
- Gavin's made a start: https://gist.github.com/gavinking/ca2fba39c73d9dc376ee
- My fork: https://gist.github.com/tombentley/888585d07a0a03a38549
What he has doesn't handle class hierarchies -- we've no way to associate the names of attributes with the class declaration they're from. This is a problem because there canbe multiple attributes with the same name in a class hierarchy.
class One(String s) {
shared String captureS => s;
}
class Two(String s) extends One("1") {
shared String captureAnotherS => s;
}
That sort of thing can't be handled with a format like this:
{ "$" -> 1;
"x" : 0.0;
"y": 1.0
}
Because it doesn't make clear that it's Point
's x
. We could use dot notation:
{ "$": 1;
"example::Point.x" : 0.0;
"example::Point.y": 1.0
}
But that makes the serialized form a lot bigger because of all the duplication of "Point." etc. Trying to shorten the keys makes the format harder to read. I suppose we could use the unambiguous format "example::Point.x"
only when there is an ambiguity, and otherwise just use "x"
, but that requires another whole load of maps because we don't know about the ambiguity until we encounter it during serialization.
I suppose another option would be to use an annotation, to control the name each attribute gets mapped to. By default it would be the simple name of the attribute. We would error when we encountered an ambiguity:
class One(s) {
jsonKey("One::s")
String s;
shared String captureS => s;
}
class Two(s) extends One("1") {
jsonKey("Two::s")
String s;
shared String captureAnotherS => s;
}
Gavin's idea doesn't persist the name of the type of the instance. That's not necessarily a problem if the stream isn't supposed to be self-describing (i.e. we know out-of-band that the stream is one of Point
s), but I suspect it is a problem really.
{ "$": 1;
"$type": "example.Point",
...
}
this is fine, but we end up needing a parser for class type names (with generics). Also that's more redundancy of class names.
Again we can reduce the redundancy of the class names by using a mapping of short name to full name, but that harms readability. Or we compress the stream in its serialized form (gzip etc).
This SL will almost certainly need to support Array
, List
, Tuple
, ArraySequence
etc. In particular, which of those gets to map to JSON's Array
type?