Appearance
Lesson 02 · JSON Binding — Jackson & Gson
Beyond the 1Z0-830 exam
JSON is the lingua franca of web APIs. Java has no built-in JSON binder, so projects use Jackson (the de-facto standard) or Gson. Binding well — and knowing the default pitfalls — is what keeps API tests readable and robust.
Objectives
After this lesson you will be able to:
- Deserialize JSON into POJOs/records and serialize objects back to JSON.
- Bind JSON arrays to typed
Lists. - Read dynamic JSON with a tree (
JsonNode) when there's no fixed schema. - Avoid the common defaults that bite — unknown properties, naming, dates.
Jackson ObjectMapper — the workhorse
java
ObjectMapper mapper = new ObjectMapper(); // create once, reuse (thread-safe)
// JSON text → object
Widget w = mapper.readValue(json, Widget.class);
// object → JSON text
String out = mapper.writeValueAsString(w);Jackson maps JSON keys to fields/record components by name. A record Widget(long id, String name, boolean inStock) binds {"id":1,"name":"Gizmo","inStock":true} with no annotations — Jackson (2.12+) supports records natively.
Arrays and generics
A JSON array needs the element type spelled out (erasure means List<Widget>.class doesn't exist):
java
List<Widget> list = mapper.readValue(json, new TypeReference<List<Widget>>() {});
// or, concisely:
List<Widget> list = mapper.readerForListOf(Widget.class).readValue(json);Trap — unknown properties fail by default
Out of the box, Jackson throws UnrecognizedPropertyException if the JSON contains a key your class has no field for. Real APIs add fields over time, so tolerate extras:
java
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// or per-type: @JsonIgnoreProperties(ignoreUnknown = true)When to use a tree instead
If the payload is dynamic, deeply nested, or you only need a field or two, bind to a tree rather than a class:
java
JsonNode root = mapper.readTree(json);
String name = root.get("name").asText();
int firstId = root.get("items").get(0).get("id").asInt();Use typed classes for a stable schema you want compile-time safety on; use a tree for exploratory or one-off reads. (RestAssured's JSON-path, next lesson, is the same idea for assertions.)
Common configuration
| Need | Jackson knob |
|---|---|
| Ignore extra keys | FAIL_ON_UNKNOWN_PROPERTIES=false / @JsonIgnoreProperties |
| Rename a field | @JsonProperty("snake_case_key") |
snake_case everywhere | setPropertyNamingStrategy(SNAKE_CASE) |
| Skip nulls in output | setSerializationInclusion(Include.NON_NULL) |
java.time types | register JavaTimeModule (jackson-datatype-jsr310) |
Gotcha — java.time needs a module
Without the JavaTimeModule registered, Jackson serializes an Instant/LocalDate as an array of numbers or throws. Register it (mapper.registerModule(new JavaTimeModule())) and disable WRITE_DATES_AS_TIMESTAMPS for ISO-8601 strings.
Gson — the lightweight alternative
java
Gson gson = new Gson();
Widget w = gson.fromJson(json, Widget.class);
String out = gson.toJson(w);Gson is smaller and simpler; Jackson is faster at scale, more configurable, and has a richer module ecosystem (XML, CSV, CBOR, streaming). Most Java shops standardize on Jackson — and so does the lab.
SDET note
The lab's ApiClient round-trips through Jackson: it writeValueAsString a record into a POST body, and readValues the response into a typed Widget. The test asserts on the deserialized object (assertThat(w).isEqualTo(new Widget(1, "Gizmo", true))), not on raw JSON text — binding makes the assertion about meaning, not formatting.
Key Takeaways
- Jackson
ObjectMapper:readValue(JSON→object) andwriteValueAsString(object→JSON); create once, reuse. - Records and POJOs bind by field name; arrays need a
TypeReferenceorreaderForListOf. - Disable
FAIL_ON_UNKNOWN_PROPERTIESso new API fields don't break your tests. - Use a
JsonNodetree for dynamic/partial reads; typed classes for a stable schema. java.timetypes need theJavaTimeModule; Gson is a simpler alternative to Jackson.
Lesson Quiz
Which ObjectMapper method turns JSON text into a typed object?
By default, Jackson encounters a JSON key with no matching field. It…
How do you deserialize a JSON array into List<Widget>?
When is binding to a JsonNode tree preferable to a typed class?
Serializing a LocalDate with a default ObjectMapper often fails or looks wrong because…
Next: REST Testing with RestAssured. This module's lab is in labs/src/main/java/com/jse21/m17_api/.