JSON to Java Class: Jackson, Gson, and POJO Generation

Last updated:

Converting JSON to Java classes is a core task in any Java backend or Android project. Jackson and Gson are the two dominant libraries, each with their own annotations and ObjectMapper/Gson APIs. For large JSON structures, tools like jsonschema2pojo generate the classes automatically. This guide covers manual POJO creation, Jackson ObjectMapper configuration, Java records, Lombok integration, Gson, and automatic class generation.

Manual POJO from JSON

Given the following JSON structure, here is how to write the corresponding Jackson POJO by hand:

{
  "user_id": 42,
  "full_name": "Alice Smith",
  "email": "alice@example.com",
  "is_active": true,
  "created_at": "2026-01-15T10:30:00Z",
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "country_code": "US"
  },
  "tags": ["admin", "beta-user"]
}
import com.fasterxml.jackson.annotation.*;
import java.time.Instant;
import java.util.List;

@JsonIgnoreProperties(ignoreUnknown = true)   // safe: ignore new API fields
public class User {
    @JsonProperty("user_id")
    private Long userId;

    @JsonProperty("full_name")
    private String fullName;

    private String email;           // same name in JSON → no annotation needed

    @JsonProperty("is_active")
    private boolean active;

    @JsonProperty("created_at")
    private Instant createdAt;

    private Address address;        // nested object — auto-mapped

    private List<String> tags;      // JSON array → List<String>

    // Jackson requires a no-args constructor OR @JsonCreator
    public User() {}

    // Getters and setters
    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }
    // ... (all fields need getters/setters for Jackson)
}

public class Address {
    private String street;
    private String city;
    @JsonProperty("country_code")
    private String countryCode;
    // getters/setters
}

Jackson ObjectMapper — Serialize and Deserialize

ObjectMapper is Jackson's central API. Create one instance, configure it once, and reuse it — it is thread-safe.

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

// Configure ObjectMapper (create once, reuse — it's thread-safe)
ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);

// JSON string → Java object
String json = "{\"user_id\": 42, \"full_name\": \"Alice\", \"email\": \"alice@example.com\"}";
User user = mapper.readValue(json, User.class);
System.out.println(user.getFullName());   // → "Alice"

// Java object → JSON string
String serialized = mapper.writeValueAsString(user);
// → {"user_id":42,"full_name":"Alice","email":"alice@example.com","is_active":false}

// Pretty-print
String pretty = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);

// Generic types (e.g., List<User>)
List<User> users = mapper.readValue(json,
    mapper.getTypeFactory().constructCollectionType(List.class, User.class));

// Map<String, Object> for dynamic JSON
Map<String, Object> map = mapper.readValue(json,
    new TypeReference<Map<String, Object>>() {});

Java Records with Jackson (Java 16+)

Java records are concise, immutable, and work with Jackson 2.12+ out of the box — no @JsonCreator or no-args constructor needed.

import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;
import java.util.List;

// Records are immutable and ideal for API response types
public record User(
    @JsonProperty("user_id") Long userId,
    @JsonProperty("full_name") String fullName,
    String email,
    @JsonProperty("is_active") boolean active,
    @JsonProperty("created_at") Instant createdAt,
    Address address,
    List<String> tags
) {}

public record Address(
    String street,
    String city,
    @JsonProperty("country_code") String countryCode
) {}

// Jackson 2.12+ handles records automatically
// No @JsonCreator or @JsonDeserialize needed
ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
User user = mapper.readValue(json, User.class);
System.out.println(user.fullName());  // record accessor, not getter

Lombok + Jackson

Lombok eliminates boilerplate getters, setters, constructors, and builders. Combine it with Jackson for a concise yet fully serializable class.

import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.Instant;

@Data                   // @Getter, @Setter, @EqualsAndHashCode, @ToString
@Builder                // User.builder().userId(1).build()
@NoArgsConstructor      // required by Jackson
@AllArgsConstructor     // required by @Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
    @JsonProperty("user_id")
    private Long userId;

    @JsonProperty("full_name")
    private String fullName;

    private String email;

    @JsonProperty("is_active")
    private boolean active;

    @JsonProperty("created_at")
    private Instant createdAt;
}

// Build and serialize
User user = User.builder()
    .userId(42L)
    .fullName("Alice")
    .email("alice@example.com")
    .active(true)
    .build();

String json = mapper.writeValueAsString(user);
// → {"user_id":42,"full_name":"Alice","email":"alice@example.com","is_active":true,...}

Gson Alternative

Gson uses @SerializedName instead of @JsonProperty and a Gson instance instead of ObjectMapper.

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;

// Gson POJO — uses @SerializedName instead of @JsonProperty
public class User {
    @SerializedName("user_id")
    private Long userId;

    @SerializedName("full_name")
    private String fullName;

    private String email;

    // No-args constructor required
    public User() {}
}

// Serialize/Deserialize
Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .create();

// JSON → Java
User user = gson.fromJson(json, User.class);

// Java → JSON
String serialized = gson.toJson(user);

// Generic types
Type userListType = new TypeToken<List<User>>() {}.getType();
List<User> users = gson.fromJson(jsonArray, userListType);

jsonschema2pojo — Generate Classes Automatically

Maven plugin that generates Java classes from JSON Schema or sample JSON at build time:

<!-- pom.xml -->
<plugin>
    <groupId>org.jsonschema2pojo</groupId>
    <artifactId>jsonschema2pojo-maven-plugin</artifactId>
    <version>1.2.1</version>
    <configuration>
        <sourceDirectory>${basedir}/src/main/resources/schema</sourceDirectory>
        <targetPackage>com.example.model</targetPackage>
        <annotationStyle>jackson2</annotationStyle>
        <includeAdditionalProperties>false</includeAdditionalProperties>
        <useLombok>true</useLombok>
        <includeConstructors>true</includeConstructors>
        <generateBuilders>true</generateBuilders>
    </configuration>
</plugin>
// src/main/resources/schema/user.json (JSON Schema input)
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "user_id": { "type": "integer" },
    "full_name": { "type": "string" },
    "email": { "type": "string", "format": "email" },
    "tags": { "type": "array", "items": { "type": "string" } }
  },
  "required": ["user_id", "full_name", "email"]
}

Also available as an online tool at jsonschema2pojo.org — paste JSON, select Jackson or Gson, download generated classes.

Handling Nested Arrays and Complex Types

// JSON: {"products": [{"id": 1, "name": "Widget", "prices": {"USD": 9.99, "EUR": 8.99}}]}

public class ProductCatalog {
    private List<Product> products;
    // getter/setter
}

public class Product {
    private Long id;
    private String name;
    private Map<String, Double> prices;    // {"USD": 9.99} → Map<String, Double>
    // getter/setter
}

// Deserialize
ProductCatalog catalog = mapper.readValue(json, ProductCatalog.class);
catalog.getProducts().get(0).getPrices().get("USD");  // → 9.99

// Handle optional/nullable fields
public class Order {
    private Long id;
    private String status;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String cancellationReason;   // null if not cancelled — omit in output
    // getter/setter
}

Definitions

POJO (Plain Old Java Object)
A simple Java class with no special framework constraints; has fields, getters, setters, and a no-args constructor. The standard format for JSON deserialization targets.
ObjectMapper
Jackson's central class for converting between Java objects and JSON. Thread-safe — create one instance and reuse it across your application. Configure once with modules and features.
TypeReference
A Jackson abstract class used to preserve generic type information at runtime, bypassing Java's type erasure. Required for deserializing generic types like List<User> or Map<String, User>.
@JsonProperty
Jackson annotation that maps a Java field to a JSON property name. Use when the JSON key differs from the Java field name, such as snake_case JSON with camelCase Java.
jsonschema2pojo
A tool that generates Java source code from JSON Schema or example JSON. Available as a Maven plugin, Gradle plugin, and online tool at jsonschema2pojo.org. Supports both Jackson and Gson annotation styles.

FAQ

How do I convert JSON to a Java class automatically?

The fastest approach is jsonschema2pojo.org — paste your JSON, select source type "JSON", choose annotation style (Jackson 2 or Gson), and download the generated Java classes. For build-time generation, add the jsonschema2pojo Maven or Gradle plugin and place a JSON Schema file in your resources directory; the plugin generates classes before compilation. IntelliJ IDEA users can install the "JSON To Pojo" plugin from the marketplace. For one-off dynamic conversion, ObjectMapper.readValue(json, Map.class) gives you a Map<String, Object> without defining a class. Choose a dedicated POJO class when you need type safety and reusability; use Map for exploratory or truly dynamic data.

What is the difference between Jackson and Gson for JSON?

Both libraries deserialize JSON to Java objects and serialize Java objects back to JSON. Jackson is the default in Spring Boot and is more feature-rich: it supports Java 8 date/time types via JavaTimeModule, handles polymorphism with @JsonTypeInfo, and its ObjectMapper is thread-safe and highly configurable. Gson is simpler with fewer dependencies, uses @SerializedName instead of @JsonProperty, and its builder API is straightforward. Jackson generally outperforms Gson on large payloads. Both libraries are thread-safe for deserialization. If you are using Spring Boot, stick with Jackson — it is already on the classpath and autoconfigured. Use Gson in Android projects or when you prefer a minimal dependency footprint.

How do I map snake_case JSON to camelCase Java fields?

With Jackson, annotate each field individually: @JsonProperty("snake_case"). For a global mapping, configure the ObjectMapper: objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) — this automatically maps user_id to userId without per-field annotations. In Spring Boot, set spring.jackson.property-naming-strategy=SNAKE_CASE in application.properties. With Gson, use @SerializedName("snake_case") on each field, or configure globally: new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(). The annotation approach is more explicit and overrides any global strategy; the global approach reduces boilerplate but applies uniformly to all classes.

What is @JsonIgnoreProperties(ignoreUnknown = true)?

It is a class-level Jackson annotation that makes deserialization silently ignore any JSON fields without a matching Java field. Without it, Jackson throws UnrecognizedPropertyException when the API response contains a field your class does not declare — a common failure mode when consuming external APIs that add new fields in newer versions. Apply it to every class that deserializes external JSON. Alternatively, configure it globally: mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false). In Spring Boot, set spring.jackson.deserialization.fail-on-unknown-properties=false for the same effect across all deserialization.

How do I deserialize a JSON array to a Java List?

With Jackson: mapper.readValue(json, new TypeReference<List<User>>() ). TypeReference is necessary because Java type erasure erases generic type parameters at runtime — List<User>.class is not valid syntax. Alternatively, use mapper.getTypeFactory().constructCollectionType(List.class, User.class). You can also deserialize as User[] and convert with Arrays.asList(). With Gson: gson.fromJson(json, new TypeToken<List<User>>() .getType()) — TypeToken serves the same purpose as TypeReference. For nested generics like List<Map<String, User>>, both TypeReference and TypeToken handle arbitrarily complex generic structures.

Can I use Java records with Jackson?

Yes, from Jackson 2.12 (released March 2021). Java records work with Jackson without special configuration: Jackson uses the canonical constructor for deserialization and the accessor methods (component names, without the "get" prefix) for serialization. Add @JsonProperty on record components to map JSON keys that differ from component names. @JsonIgnoreProperties(ignoreUnknown = true) works at the record level too. Spring Boot 2.7+ ships with Jackson 2.13+, so records work out of the box in any recent Spring application. Records are ideal for API response types because they are immutable, concise, and automatically recognized by Jackson.

How do I handle null values in JSON with Jackson?

Null JSON values map to Java null by default. To exclude null fields from serialization output, annotate the field with @JsonInclude(JsonInclude.Include.NON_NULL), or apply it at the class level to affect all fields. For optional fields, use Optional<T> with the Jdk8Module. To ignore null values during deserialization (leaving the Java field at its default), use @JsonSetter(nulls = Nulls.SKIP). Configure globally: mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL) omits all null fields from every serialized object. Distinguish carefully between an absent field (key not in JSON) and an explicit null value — they are different states that may require different handling in your domain logic.

What is TypeReference in Jackson?

TypeReference is an abstract class in Jackson (com.fasterxml.jackson.core.type.TypeReference) used to work around Java generic type erasure. At runtime, the JVM discards generic type parameters — List<User> and List<String> are both just List at the bytecode level. By instantiating an anonymous subclass (new TypeReference<List<User>>() ), the generic type is baked into the subclass's type signature and can be retrieved via reflection at runtime. TypeReference is only needed for generic types — for non-generic types, pass the class directly: mapper.readValue(json, User.class). Gson's equivalent is TypeToken: new TypeToken<List<User>>() .getType().

Further reading and primary sources

  • Jackson DocumentationOfficial Jackson databind wiki: ObjectMapper configuration, annotations, and advanced features
  • Gson User GuideComplete Gson guide: serialization, deserialization, TypeToken, custom serializers, and GsonBuilder
  • jsonschema2pojoOnline tool and Maven/Gradle plugin to generate Java classes from JSON Schema or example JSON