Beyond the question of how to integrate this into Foundation, I would like to suggest a concrete idea about how the open implementation could/should look like.
The strategy Iâm going to describe is already implemented and tested up to some point as MetaSerialization here.
As I tried to implement a Encoder
and Decoder
for msgpack, I realised, that the main work of _JSONEncoder
seems to be to convert itâs Codable
input to a representation that JSONSerialization
is able to work with. The critical point here is in my understanding, that other underlying serialization libraries will need different representations and may often want to avoid NSDictionary
, NSArray
, NSString
, etc, for which the implementor would still need to override large parts of the open Encoder
implementation. But how this representation looks like is mainly related to the âprimitiveâ types of the serialization format. Those primitive types are also the types the Encoder
needs to convert to and the Decoder
needs to convert from. The code that is involved in this are the various wrap
and unwrap
functions.
(Note that there is also some code especially in the unwrap function, that should be common to most implementations: Detecting instances that arenât able to decode itself and throw an error for these instances, to prevent endless loops)
Now what I did in MetaSerialization is providing MetaEncoder
and MetaDecoder
(as open classes, for further customisation) that handle the coding storage (called CodingStack in MetaSerialization) and the various container methods (there are therefor also open implementations for these container protocols). MetaEncoder
and MetaDecoder
rely on an instance of the protocol Translator
, that the user of the framework needs to provide. This Translator
defines the primitive types and handles the encoding/decoding step. The current implementation requires it to implement four methods (two of which have defaults) in order to set and handle primitive types. wrappingMeta
returns a wrapper for a certain type in the encoding step, if this type is primitive (the wrapper is called Meta and needs to conform to protocol Meta
) or returns nil, if a type isnât primitive. MetaEncoder
calls this function for every value it sees and directly pushes this meta to itâs coding stack, or continues to encode the value, if it getâs nil. Finally all types need to be converted to primitive types, or (un)keyed containers of primitive types. The implementations that are actually used for these containers are also set by the Translator in two functions called keyedContainer
and unkeyedContainer
.
When decoding, MetaDecoder asks the Translator to unwrap a certain Meta to a certain type. Translator may return nil, if it canât unwrap to this type, or returns the wrapped value, if the type can be converted from the stored. MetaDecoder detects types that produce endless loops, because they arenât supported but have not strategy to decode themselves from any other representation.
These ideas and the implementation of them isnât finished yet and Iâm not entirely fine with the Translator
protocol, because I think it could be simpler.
A simple example, that demonstrates a verry simple way to use meta-serialization can be found in the playground contained in the repository. However, this example does not show how to implement Translator
. There is another example in a separate repository that implements Translator and also shows concrete imolementations and use cases for custom implementations of Meta
.
I hope that this description gives a new idea about it could be implemented.