Decode a JSON object of unknown format into a Dictionary with Decodable in Swift 4

We can certainly add some dynamic lookup features to Unevaluated to make it more accessible, and I think that would be a great extension to the feature (and a good use-case for dynamic lookup)! However, I think we'd rather avoid adding JSON-specific types for this.

When designing Codable, one of the big decisions we made early on was that we wanted Codable to be as format-agnostic as we could make it (without making it so abstract as to no longer be helpful); our goal was to help abstract away just enough details to make a given Codable implementation work with many different formats. As such, many existing Codable implementations would work as-is whether encoded through JSONEncoder, PropertyListEncoder, or any other Encoder you might write.

With this, I recognize that the #1 use-case by far for Codable at the moment is for serializing to and from JSON; it's a really popular format at the moment, and when most folks write their Codable implementations, they've got JSON in mind. However, what we'd like to avoid is facilitating writing something like

init(from decoder: Decoder) throws {
    let container = try decoder.singleValueContainer()
    let contents = try container.decode(JSONDecoder.UnevaluatedJSON.self)
    // ... inspect contents
}

Because the Unevaluated type is essentially just a marker type, scoping such a type too closely to one format makes the Codable implementation unusable for other formats. Specializing parts of a Codable implementation is possible, but error-prone:

init(from decoder: Decoder) throws {
    if decoder is JSONDecoder {
        // do JSON stuff
    } else {
        // do general stuff
    }
}

The above will fail because the Decoder passed in is not a JSONDecoder; JSONDecoder uses a private class _JSONDecoder to do the decoding work, so this is easy to get wrong. At the moment, the right way to do this would be to pass a known value through the JSONDecoder's userInfo (which you control) and check for that type through decoder.userInfo. (There's also currently no way to query a decoder for its format, so the solution there is the same. One of the enhancements I want to make is along this vein, to improve the ergonomics of checking this.)

In any case, our philosophy on this is generally that if JSON can benefit from such a feature, other formats should be able to benefit as well. We'd rather not gate this on JSON adoption specifically, especially as we're looking to add new encoders and decoders ourselves to support further formats.

Is there something specific you'd like to see from dynamic lookup that would make the ergonomics of Unevaluated better? Happy to get input here!

2 Likes