Working with JSON
Working with JSON
JSON is the lingua franca of modern APIs. Every REST client you write will parse JSON responses and serialise Java objects into JSON request bodies. In this lesson you will learn how to do both professionally using Jackson, the de-facto standard library for JSON in the Java ecosystem, alongside the built-in org.json approach and a brief look at Gson so you can evaluate trade-offs and make informed choices.
Why Jackson?
Jackson is used by Spring Boot, Quarkus, Micronaut, and virtually every other major Java framework as their default JSON engine. It is battle-tested, extremely fast, and rich in features. The core artifact is jackson-databind, which pulls in jackson-core and jackson-annotations:
org.json is a lightweight, dependency-free option for ad-hoc parsing but has no data-binding. Jackson has the richest feature set and best performance at scale — prefer it for any production service.
The ObjectMapper — One Instance, Thread-Safe
ObjectMapper is Jackson's workhorse. It is expensive to create and thread-safe after configuration, so create a single shared instance (e.g. as a static final field or a singleton bean):
Deserialising JSON into Java Objects (Reading)
Given a JSON string from an HTTP response body, ObjectMapper.readValue() maps it to a typed Java object. First define the target class using a Java record (preferred in Java 17+) or a POJO with getters:
Handling JSON Arrays and Generic Collections
When the API returns a JSON array, you need a TypeReference to preserve the generic type at runtime (Java erases generics at bytecode level, so List<Product>.class is not a valid expression):
TypeReference for any generic type. It works equally well for Map<String, Object>, List<Map<String, String>>, or any other parameterised type. The anonymous subclass syntax {} at the end is intentional — it captures the generic type signature in bytecode so Jackson can read it via reflection.
Serialising Java Objects into JSON (Writing)
The reverse operation — turning a Java object into a JSON string for a request body — uses writeValueAsString():
Controlling the Mapping with Annotations
Real APIs rarely have JSON keys that match Java naming conventions perfectly. Jackson provides annotations to bridge the gap:
@JsonProperty("key")— renames the field in JSON. Essential when the API uses snake_case and Java uses camelCase.@JsonIgnore— excludes the field from both serialisation and deserialisation. Use for sensitive data like password hashes.@JsonInclude(NON_NULL)— suppresses null fields in the output, keeping the JSON compact.@JsonAlias— accepts multiple names during deserialisation (useful when an API changes a field name and you must support both versions during a transition).
Global Snake-Case Naming Strategy
Rather than annotating every field, you can configure ObjectMapper globally to translate between camelCase (Java) and snake_case (API) automatically:
Reading Arbitrary JSON with JsonNode
Sometimes you do not have — or do not want — a fixed schema. JsonNode gives you a dynamic tree model, similar to a DOM for JSON. This is valuable for inspecting API responses during development or for processing heterogeneous data:
path() over get() when navigating optional fields. get() returns null for a missing key, which will throw a NullPointerException on the next call. path() returns a MissingNode sentinel that is safe to traverse further and produces a default value when converted with asText(), asInt(), etc.
Producing JSON with ObjectNode
When you need to build a JSON payload dynamically (fields determined at runtime), use ObjectNode instead of string concatenation:
Handling Unknown Fields Gracefully
APIs evolve and sometimes return fields your Java class does not model. By default, Jackson throws UnrecognizedPropertyException for unknown fields. In production code, configure the mapper to ignore them so your client is not broken by harmless API additions:
Jackson vs Gson — When to Choose What
- Jackson — best choice for production services, framework integration, streaming large payloads, and rich annotation-based control.
- Gson — simpler API, zero configuration for basic use, good for Android or small utilities where you cannot use the full Jackson stack.
- org.json — dependency-free, good for scripting or tools where adding a library is not acceptable. Has no data-binding; you access fields manually by name.
Summary
You now have a complete toolkit for JSON in Java: create a single shared ObjectMapper, deserialise responses with readValue() and TypeReference, serialise payloads with writeValueAsString(), control mapping with annotations, navigate unknown structures with JsonNode, and build dynamic payloads with ObjectNode. These patterns cover the vast majority of real-world REST client JSON work. The next lesson ties everything together: building a complete, production-quality REST API client.