There's also Play-Json, Spray-Json, various wrappers for Jackson and a few others I don't remember. We use Play-Json and its macros a lot at the company I work for and I contributed a performance fix for it a while back. I wouldn't consider Play's JSON inception to be "pretty involved" at all if you're using case classes.
JSON is always going to be a bit more difficult to parse in statically typed languages than dynamically typed ones.
The amusing part is that most APIs use JSON as if it were statically typed - except for null/undefined, very few APIs return values of more than one possible type for a given key[0].
It doesn't always have to be so bad, though, even in statically typed languages. For example, I created a tool in Go to automatically generate struct definitions, given an example JSON response: https://github.com/ChimeraCoder/gojson
In the time that I've been using it (since last December), I don't think I've run into any issues with an API returning an incorrect type for a particular key[1].
[0] For the record, I'm not complaining. The alternative would be a nightmare to deal with.
[1] Though they do sometimes return a different object altogether; this is an issue regardless of which type system you use.
Given that it uses reflect I bet your library very slow. I wonder how it compares to parsing JSON in Python or node.js.
> except for null/undefined,
That's not a small issue.
Not only do JSON responses for API's vary a great deal from request to request, some handle errors without HTTP response code's. So the JSON you get back can be very different then you might expect otherwise.
> Given that it uses reflect I bet your library very slow.
Look carefully - what I wrote is not a library; it's a standalone binary. It's run exactly once, when you write the code, so speed isn't an issue.
> That's not a small issue.
Depends on the language. In Go, it's very easy to allow a JSON value to be null and/or undefined by making the value it unmarshals to a pointer (which you should be doing already anyway).
What's harder (in most languages, not just Go) is dealing with a value that could be (say) either a string, or a 64-bit int. Or, worse, a string whose value just happens to be a 64-bit number. When that happens (which is thankfully rare), it breaks the "JSON should be self-documenting" motto.
> So the JSON you get back can be very different then you might expect otherwise.
This is an issue no matter what language you use. You have to know what the set of possible response structures is in order to know what keys to query/access.
Incidentally, the one place I often have to deal with values of different types for the same key is from a java app that is poorly converting XML -> JSON and converts a list with a single element into just the element.