Stage 7: Tools and testing, lesson 7 of 7

JSON with Jackson

Intermediate3 min read@since 16Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

Jackson is Java's standard JSON library and is built into Spring Boot, so @RestController methods turn objects into JSON automatically.

With an ObjectMapper (Spring Boot 4 uses Jackson 3's JsonMapper, with the same ideas):

  • writeValueAsString(obj): Java to JSON (serialisation)
  • readValue(json, Order.class): JSON to Java (deserialisation)
  • readValue(json, new TypeReference<List<Order>>() {}) for generic types
  • readTree(json) to walk JSON you don't have a class for

Records work out of the box. Common annotations:

  • @JsonProperty("order_id") maps a different JSON name.
  • @JsonIgnore hides a field (passwords, internal notes).
  • @JsonIgnoreProperties(ignoreUnknown = true) tolerates extra fields from other APIs.
  • @JsonFormat controls date formats.

Create one ObjectMapper, configure it and reuse it: it's thread-safe and costly to build.

Example

Java
public record Order(
        @JsonProperty("order_id") String id,
        String customer,
        BigDecimal amount,
        LocalDate placedOn,
        @JsonIgnore String internalNote) {}

ObjectMapper mapper = JsonMapper.builder()
        .addModule(new JavaTimeModule())                    // Jackson 2: java.time support
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
        .build();

String json = mapper.writeValueAsString(
        new Order("ORD-7", "Asha", new BigDecimal("1499.00"), LocalDate.of(2026, 9, 24), "vip"));
// {"order_id":"ORD-7","customer":"Asha","amount":1499.00,"placedOn":"2026-09-24"}

Order back = mapper.readValue(json, Order.class);
List<Order> many = mapper.readValue("[" + json + "]", new TypeReference<List<Order>>() {});

JsonNode node = mapper.readTree("{\"user\":{\"name\":\"Ravi\",\"tags\":[\"java\",\"spring\"]}}");
String name = node.path("user").path("name").asText();     // Ravi

Common mistake

Creating a new ObjectMapper for every request. It's thread-safe and costly to build; configure one and reuse it, or inject Spring's.

Under the hood

Never deserialise untrusted JSON into polymorphic types with default typing turned on; it has caused remote-code-execution vulnerabilities. Keep API classes (DTOs) separate from JPA entities so lazy associations and internal fields never leak into responses. Jackson 3 (used by Spring Boot 4) moved to the tools.jackson package, supports java.time by default and uses unchecked exceptions, while the annotations keep their old package.

Check yourself

Which annotation keeps a field out of the JSON?

How this connects

Part of Job-ready backend developer, Upgrade from Java 8 to Java 25.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.