(1) Expect it to conform to a contract. Marshal and unmarshal it to and from a backing data structure that represents that contract. Perhaps with some tolerance for missing or unexpected values.
(2) Have no expectations of a contract. Parse the JSON string into a blob-tree of your programming language's "any" type. Just kinda yolo-wing-it with tree traversal, and hope that your language syntax is very null-tolerant. Try to overlook the fact that ultimately you DO have an expectation of contract after all (!)... it's just scattered through your tree-traversal logic, rather than consolidated in a data structure.
I think pretty much all modern widely-used languages do a decent enough job with approach #1. While approach #2 is pretty much equally awful with all languages.
Also, there's a sort of intermediate step, a dynamic language with schema validation, for which I wrote a modestly well-used (within the overall schema validation gem market) gem a while ago. Then you can validate at the point of translation, without being beholden to a concrete data type (POJO, data class, case class, etc.) that has to be updated whenever the wire format changes.
"Readability" and "simplicity" are except for trivial or outlandish cases subjective.
For example, I rather enjoy reading code using Rust's serde crate: the code is quite readable and comes with all the benefits of Rust's static type system and safety.
Some are far more awful than others. Python for example has no issue here at all, json types map nicely into its basic data types, while most statically typed languages make json handling terribly verbose and make it feel like it doesn't belong there. And while there usually is some contract, there are many api providers that don't provide schema, make frequent changes or explicitely expect you to deal with "some random json here" part of the data.
Your data comes out of the JSON structure wrapped in a functor, and you can only access it in ways that deal with missing data.
It’s hypothetically possible to recreate this approach in Python, but I don’t think it would be Pythonic.
The functor approach doesn't involve any type casting, and null pointer exceptions are impossible.
int JsonValue::getAsInt(int defaultIfError);
Or, in Go:
func (JsonValue*) AsInt() (int64, error)
Or are you just talking about it returning the value as a Rust-style Result sum type?
Its not idiomatic in most mainstream languages. Except, that's how most JSON libraries which validate against a static structure are implemented, they just abstract the reflection away via some interface.
Yes, I didn't mean to imply that I was providing an exhaustive list of languages where this approach could be implemented. I merely mentioned Python because it had been brought up several times in the thread.
> Grab the item in the map at that key -> check that it exists -> check that the type is what you expect.
I'd argue with that phrasing. You don't check for the key, nor do you check the type. That would imply something like an if-statement to ensure the presence of a key, and switching over a type. In such a system, you could forget to check for null, or make some other mistake that causes the program to fail at run time.
The functor approach does not rely on the developer checking anything. It is 100% type safe, enforced by the type system, cannot crash at run time, and does not require you to provide a data type to extract the data into.
https://fsharp.github.io/FSharp.Data/
https://medium.com/@maximcus/magic-of-f-type-providers-225b1...