The primary goal of a Codable
conformance for any type is to ensure safe & reliable round tripping through serialization.
OrderedDictionary
is built around the notion that the ordering of items is significant -- risking ordering to be lost during a serialization round trip seems like an absurd ask to me.
Indeed. The Codable
family of protocols do not guarantee that a keyed container must preserve ordering during an encode/transmit/decode round trip. (And indeed they can't, since e.g. technically neither does JSON.)
Therefore, OrderedDictionary
cannot, by default, encode itself into a keyed container. Its current implementation is the correct way to guarantee that ordering is preserved after an encode/decode round trip. This implementation is obviously useful for serialization purposes.
The wrapper suggestion is workable, as long as we only use it at the coding boundaries, as otherwise itâs rather hard to use since it canât do everything the underlying collection can.
Yep.
If for some reason you want/need to serialize or deserialize an ordered dictionary to/from a keyed container (for example, because you know you're always going to be using an order-preserving serializer/deserializer, and you have external constraints on the precise way your data gets serialized -- which, I have to note, isn't a use case Codable was specifically designed to cover*), then one simple way to do that is to wrap the collection into an adapter type on the fly, within the encode/decode members:
func encode(to encoder: Encoder) throws {
...
try keyedContainer.encode(KeyedEncodingAdapter(orderedDict), forKey: .dict)
...
}
init(from decoder: Decoder) throws {
...
orderedDict = try keyedContainer.decode(
KeyedEncodingAdapter<Key, Value>.self,
forKey: .dict
).value
...
}
You can also define your own extension methods on Keyed[Encoding,Decoding]Container
to achieve this less verbosely:
try keyedContainer.encodeIntoKeyedContainer(orderedDict, forKey: .dict)
...
orderedDict = try keyedContainer.decodeFromKeyedContainer(orderedDict, forKey: .dict)
Of course, this means you cannot rely on compiler synthesis, which, given the risks associated with using a keyed container in this context, seems like a good thing! Assumptions are best when they're made explicitly.
* (A serialization facility that was designed to provide direct control over the serialization output would most likely be (1) explicitly designed around that specific serialization format, and (2) would have extensive hooks for convenient remapping of names and customizing all other details of the serialization.)