JSON in Java

Updated: August 8, 2026

Mapping JSON to Java objects with Jackson and Gson — configuration, annotations, and the defaults that will bite you in production.

Java Has No Built-In JSON Support

This surprises developers arriving from Python or JavaScript, where JSON is one import away. Java SE ships no JSON API at all, so every Java project that touches JSON picks a library. Three matter in practice:

  • Jackson — the default choice, and what Spring Boot autoconfigures. Fastest on large documents, extensive annotation support, modular.
  • Gson — Google's library. Smaller, simpler API, popular on Android. Slower on large payloads but easier to pick up.
  • JSON-B / JSON-P — the Jakarta EE standards. Worth using if you are already inside a Jakarta container and want to avoid a third-party dependency; rarely chosen otherwise.

Unless you have a specific reason, use Jackson. The rest of this page covers it primarily, with Gson equivalents where they differ meaningfully.

The Basics

import com.fasterxml.jackson.databind.ObjectMapper;

// ObjectMapper is thread-safe and expensive to create.
// Build one, reuse it for the life of the application.
private static final ObjectMapper MAPPER = new ObjectMapper();

// Java object → JSON
String json = MAPPER.writeValueAsString(user);

// Pretty printed
String pretty = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(user);

// JSON → Java object
User user = MAPPER.readValue(json, User.class);

// From a file or URL
User user = MAPPER.readValue(new File("user.json"), User.class);

The reuse point is not a micro-optimisation. Constructing an ObjectMapper involves building and caching serialiser metadata; creating one per request is a well-known cause of poor throughput in Java services. Make it a static final field or a Spring bean.

Configure It Before You Ship It

Jackson's defaults are conservative in ways that cause production incidents. These four settings are worth applying to nearly every application mapper:

ObjectMapper mapper = new ObjectMapper()
    // Do not explode when the provider adds a field you don't know about
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

    // Omit null fields from output instead of emitting "field": null
    .setSerializationInclusion(JsonInclude.Include.NON_NULL)

    // Understand java.time (Instant, LocalDate, OffsetDateTime…)
    .registerModule(new JavaTimeModule())

    // Emit ISO 8601 strings, not numeric epoch timestamps
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

The first is the most important. By default, Jackson throws UnrecognizedPropertyException when JSON contains a property your class does not declare. That means any upstream service adding a harmless new field breaks your client on deploy. Forward compatibility — ignoring what you do not understand — is almost always the behaviour you want from a consumer.

The third and fourth go together. Without JavaTimeModule registered, serialising an Instant either fails or produces a sprawling object of internal fields. With it but without disabling WRITE_DATES_AS_TIMESTAMPS, you get numeric epochs — valid, but painful for anyone reading the payload.

Mapping Names and Fields

Jackson matches JSON keys to bean properties by name. When the JSON uses a different convention — and APIs very often use snake_case where Java uses camelCase — you bridge the gap with annotations or a global strategy.

public class User {
    private long id;

    @JsonProperty("full_name")      // maps JSON full_name → fullName
    private String fullName;

    @JsonIgnore                      // never serialise this field
    private String passwordHash;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<String> roles;

    // A no-arg constructor plus getters/setters are required
    public User() {}
    public long getId() { return id; }
    public void setId(long id) { this.id = id; }
    // …
}

If the whole API uses snake_case, set it once instead of annotating every field:

mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);

@JsonIgnore on sensitive fields is a security control, not a formatting choice. A password hash or internal token that reaches a serialiser will end up in a response body and then in someone's logs.

Generics and Type Erasure

Deserialising into a collection is where Java's type erasure becomes visible. readValue(json, List.class) compiles happily and gives you a List<LinkedHashMap> — not the list of objects you wanted, and the failure shows up as a ClassCastException somewhere else entirely.

// Wrong — element type is erased, you get maps
List<User> users = MAPPER.readValue(json, List.class);

// Right — TypeReference preserves the generic parameter
List<User> users = MAPPER.readValue(json, new TypeReference<List<User>>() {});

Map<String, List<User>> grouped =
    MAPPER.readValue(json, new TypeReference<Map<String, List<User>>>() {});

The anonymous subclass in TypeReference is what captures the type argument at runtime. Gson uses the same trick with TypeToken.

Gson, Briefly

Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .serializeNulls()
    .create();

String json = gson.toJson(user);
User user = gson.fromJson(json, User.class);

// Generics, via TypeToken
Type listType = new TypeToken<List<User>>(){}.getType();
List<User> users = gson.fromJson(json, listType);

Two behavioural differences worth knowing. Gson ignores unknown properties silently by default, where Jackson throws — Gson's behaviour is friendlier but hides genuine contract drift. And Gson omits null fields by default, where Jackson includes them unless configured otherwise. Neither default is wrong, but assuming one library's behaviour while using the other produces confusing bugs.

Large Documents

readValue materialises the whole document. For files too large to hold in memory, Jackson's streaming API reads token by token at constant cost:

try (JsonParser parser = MAPPER.getFactory().createParser(new File("huge.json"))) {
    while (parser.nextToken() != JsonToken.END_ARRAY) {
        if (parser.currentToken() == JsonToken.START_OBJECT) {
            User user = MAPPER.readValue(parser, User.class);
            process(user);   // one record at a time
        }
    }
}

If you control the format, JSON Lines — one document per line — is simpler still, and lets you process the file with an ordinary BufferedReader.

Frequently Asked Questions

Does Java have a built-in JSON parser?

Not in Java SE. Unlike Python or JavaScript, the standard library ships no JSON support, so you must add a dependency. Jackson is the de facto default and is what Spring Boot uses; Gson is the lighter alternative. Jakarta EE provides JSON-P and JSON-B, but only in an enterprise container.

Should I use Jackson or Gson?

Jackson for most projects — it is faster on large payloads, has richer annotation support, and comes preconfigured in Spring Boot. Gson is a good fit for Android and small utilities where a smaller dependency and simpler API matter more than throughput.

Why does Jackson throw UnrecognizedPropertyException?

By default Jackson fails when the JSON contains a field your class does not declare. This is a deliberate safety default, but it makes your client brittle — a provider adding a new field breaks you. Disable FAIL_ON_UNKNOWN_PROPERTIES, or annotate the class with @JsonIgnoreProperties(ignoreUnknown = true).

Why are my fields null after deserialisation?

Usually a name mismatch, a missing no-argument constructor, or missing getters and setters. Jackson matches JSON keys to bean property names, so a JSON key of 'full_name' will not populate a field called fullName unless you annotate it with @JsonProperty or configure a snake_case naming strategy.

How do I handle dates in Java JSON?

Register the JavaTimeModule so Jackson understands java.time types, and disable WRITE_DATES_AS_TIMESTAMPS so they serialise as ISO 8601 strings rather than numeric epochs. Without the module, serialising an Instant or LocalDate throws or produces unusable output.

Related Resources

Related Resources