DevToolBox무료
블로그

JSON을 Java 클래스로 변환: Jackson POJO 생성 완전 가이드

12분 읽기by DevToolBox

TL;DR

A JSON to Java class tool generates annotated POJOs, Lombok data classes, or Java Records from any JSON — eliminating manual coding and preventing type errors in your Spring Boot or Android projects. Try our free JSON to Java Class tool →

Key Takeaways

  • Use @JsonProperty to map JSON snake_case keys to Java camelCase fields without renaming your field.
  • Always add @JsonIgnoreProperties(ignoreUnknown = true) to avoid breaking on API changes.
  • For generic collections, use TypeReference with Jackson or TypeToken with Gson to preserve generic type info.
  • Lombok @Data + @NoArgsConstructor reduces boilerplate while staying Jackson-compatible.
  • Java Records (Java 16+) provide immutable POJOs with minimal code; add @JsonCreator for custom deserialization.
  • Register JavaTimeModule to handle LocalDateTime and ZonedDateTime without numeric timestamps.

Why Convert JSON to Java Classes?

JSON is the universal data exchange format for REST APIs, configuration files, and messaging queues. When you consume a JSON API in Java, you have three main approaches: parse it manually with aJSONObject, work with rawMap<String, Object> values, or deserialize into a strongly typed Java class. The third option is almost always the right one.

POJO vs Records vs Lombok

ApproachBoilerplateMutableBest For
Plain POJOHighYesLegacy code, JPA entities
Lombok @DataLowYesSpring Boot services, DTOs
Java RecordMinimalNoImmutable DTOs, value objects

Type-safe Java classes give you compile-time checking, IDE autocompletion, refactoring support, and self-documenting code. The Jackson and Gson libraries make the JSON-to-Java mapping automatic through annotations — no manual field extraction required.

Generate Java classes from JSON instantly with our free tool →

Java POJO Structure with Jackson Annotations

A production-ready POJO for JSON deserialization uses Jackson annotations to control field mapping and robustness. Given this JSON:

{
  "user_id": 42,
  "first_name": "Alice",
  "last_name": "Smith",
  "email_address": "alice@example.com",
  "is_active": true
}

The corresponding Java POJO:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class User {

    @JsonProperty("user_id")
    private int userId;

    @JsonProperty("first_name")
    private String firstName;

    @JsonProperty("last_name")
    private String lastName;

    @JsonProperty("email_address")
    private String emailAddress;

    @JsonProperty("is_active")
    private boolean isActive;

    // Required by Jackson for deserialization
    public User() {}

    public User(int userId, String firstName, String lastName,
                String emailAddress, boolean isActive) {
        this.userId = userId;
        this.firstName = firstName;
        this.lastName = lastName;
        this.emailAddress = emailAddress;
        this.isActive = isActive;
    }

    // Getters and setters
    public int getUserId() { return userId; }
    public void setUserId(int userId) { this.userId = userId; }

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }

    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    public String getEmailAddress() { return emailAddress; }
    public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; }

    public boolean isActive() { return isActive; }
    public void setActive(boolean isActive) { this.isActive = isActive; }
}

Jackson ObjectMapper: Full Integration Guide

Jackson's ObjectMapper is the central class for all JSON operations. It is thread-safe after configuration and should be a singleton (or Spring bean) in your application.

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

// Configure ObjectMapper (do this once, reuse everywhere)
ObjectMapper mapper = new ObjectMapper()
    .registerModule(new JavaTimeModule())
    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    .enable(SerializationFeature.INDENT_OUTPUT);  // pretty print

// JSON string to Java object
String json = "{\"user_id\": 42, \"first_name\": \"Alice\"}";
User user = mapper.readValue(json, User.class);

// Java object to JSON string
String output = mapper.writeValueAsString(user);
// {"user_id":42,"first_name":"Alice",...}

// Read from File
User fromFile = mapper.readValue(new File("user.json"), User.class);

// Read from URL
User fromUrl = mapper.readValue(
    new URL("https://api.example.com/users/1"), User.class);

// Read from InputStream (e.g., HTTP response body)
User fromStream = mapper.readValue(inputStream, User.class);

Maven Dependency

<!-- pom.xml -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.17.2</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.datatype</groupId>
  <artifactId>jackson-datatype-jsr310</artifactId>
  <version>2.17.2</version>
</dependency>

Lombok Integration: Eliminating Boilerplate

Lombok generates getters, setters, constructors, and builders at compile time through annotations. The sameUser POJO with Lombok:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data                    // generates getters, setters, equals, hashCode, toString
@Builder                 // fluent builder: User.builder().userId(42).build()
@NoArgsConstructor       // required by Jackson for deserialization
@AllArgsConstructor      // used by @Builder internally
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {

    @JsonProperty("user_id")
    private int userId;

    @JsonProperty("first_name")
    private String firstName;

    @JsonProperty("last_name")
    private String lastName;

    @JsonProperty("email_address")
    private String emailAddress;

    @JsonProperty("is_active")
    private boolean active;  // getter: isActive(), setter: setActive()
}

Builder usage for cleaner test data construction:

User user = User.builder()
    .userId(42)
    .firstName("Alice")
    .lastName("Smith")
    .emailAddress("alice@example.com")
    .active(true)
    .build();
Lombok Gradle setup: compileOnly 'org.projectlombok:lombok:1.18.34' and annotationProcessor 'org.projectlombok:lombok:1.18.34'in your build.gradle.

Java Records (Java 16+): Immutable Data Carriers

Java Records are a concise syntax for immutable data classes. They are ideal for API response DTOs where you don't need to modify the object after creation. Jackson supports Records natively from version 2.12:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

// Java Record — all fields are final, accessor methods auto-generated
@JsonIgnoreProperties(ignoreUnknown = true)
public record UserRecord(
    @JsonProperty("user_id") int userId,
    @JsonProperty("first_name") String firstName,
    @JsonProperty("last_name") String lastName,
    @JsonProperty("email_address") String emailAddress,
    @JsonProperty("is_active") boolean active
) {}

// Usage
ObjectMapper mapper = new ObjectMapper();
UserRecord user = mapper.readValue(json, UserRecord.class);
System.out.println(user.firstName()); // accessor, not getFirstName()
String out = mapper.writeValueAsString(user);

For Records that need a custom constructor (e.g., validation or transformation), use@JsonCreator:

public record Product(String name, double price) {
    // Compact canonical constructor for validation
    public Product {
        if (price < 0) throw new IllegalArgumentException("Price cannot be negative");
        name = name.trim();
    }

    // Jackson uses @JsonCreator when field names differ from record components
    @JsonCreator
    public static Product of(
        @JsonProperty("product_name") String name,
        @JsonProperty("unit_price") double price
    ) {
        return new Product(name, price);
    }
}

Nested Objects and Arrays

Real-world JSON often contains nested objects and arrays. Jackson handles these automatically by matching the Java type structure to the JSON structure:

// JSON:
// {
//   "order_id": "ORD-001",
//   "customer": { "name": "Alice", "email": "alice@example.com" },
//   "items": [
//     { "sku": "PROD-1", "quantity": 2, "price": 19.99 },
//     { "sku": "PROD-2", "quantity": 1, "price": 49.99 }
//   ],
//   "tags": ["express", "gift"],
//   "metadata": { "source": "web", "campaign": "sale2026" }
// }

@Data @NoArgsConstructor @AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class Order {

    @JsonProperty("order_id")
    private String orderId;

    private Customer customer;  // nested POJO - field name matches JSON key

    private List<OrderItem> items;  // JSON array

    private List<String> tags;  // array of primitives

    private Map<String, String> metadata;  // dynamic key-value pairs
}

@Data @NoArgsConstructor @AllArgsConstructor
public class Customer {
    private String name;
    private String email;
}

@Data @NoArgsConstructor @AllArgsConstructor
public class OrderItem {
    private String sku;
    private int quantity;
    private double price;
}

// Deserialization
Order order = mapper.readValue(json, Order.class);
System.out.println(order.getItems().size()); // 2
System.out.println(order.getMetadata().get("source")); // "web"

Generic List Deserialization

// Deserializing a JSON array at the top level
String jsonArray = "[{\"name\":\"Alice\"},{\"name\":\"Bob\"}]";

// Use TypeReference to preserve generic type parameter
List<User> users = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});

// Or use JavaType
JavaType listType = mapper.getTypeFactory()
    .constructCollectionType(List.class, User.class);
List<User> users2 = mapper.readValue(jsonArray, listType);

Optional and Nullable Fields

Handling optional fields correctly prevents NullPointerExceptions and produces clean JSON output without redundant null values:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;

// NON_NULL: exclude fields that are null from serialized JSON
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Profile {

    private String username;      // required
    private String bio;           // optional — omitted from JSON when null
    private String avatarUrl;     // optional

    @JsonInclude(Include.NON_EMPTY)
    private List<String> roles;   // excluded if null or empty list

    // Optional<T> support — requires Jdk8Module
    // mapper.registerModule(new Jdk8Module());
    private Optional<String> phoneNumber = Optional.empty();
}
Best practice: Use @JsonInclude(NON_NULL) at the class level for DTOs sent to clients, so the response JSON is lean. Keep full nulls in internal domain objects where explicitness matters more than payload size.

Gson Alternative: Google's JSON Library

Gson is Google's JSON library, widely used in Android and simpler Java projects. It uses@SerializedName instead of@JsonProperty, and TypeToken instead of TypeReference:

import com.google.gson.annotations.SerializedName;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;

public class UserGson {

    @SerializedName("user_id")
    private int userId;

    @SerializedName("first_name")
    private String firstName;

    @SerializedName("email_address")
    private String emailAddress;
}

// Configure Gson
Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .serializeNulls()                // include null fields
    .setDateFormat("yyyy-MM-dd")
    .create();

// Deserialize single object
String json = "{\"user_id\":1,\"first_name\":\"Alice\"}";
UserGson user = gson.fromJson(json, UserGson.class);

// Serialize back to JSON
String output = gson.toJson(user);

// Deserialize a List (TypeToken preserves generic type)
String jsonArray = "[{\"user_id\":1},{\"user_id\":2}]";
Type listType = new TypeToken<List<UserGson>>(){}.getType();
List<UserGson> users = gson.fromJson(jsonArray, listType);

JSON to Java Tools and Automation

For large JSON schemas with dozens of nested objects, manual POJO creation is error-prone. Several tools automate the generation:

jsonschema2pojo (Maven Plugin)

<!-- pom.xml — generates POJOs from JSON schema at build time -->
<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>
    <useLombokAnnotations>true</useLombokAnnotations>  <!-- Lombok support -->
    <generateBuilders>true</generateBuilders>
  </configuration>
  <executions>
    <execution>
      <goals><goal>generate</goal></goals>
    </execution>
  </executions>
</plugin>

IntelliJ IDEA JSON Plugin

In IntelliJ IDEA Ultimate, paste a JSON string and use Edit → Paste JSON as Java Class. The community plugin JSON To Kotlin Class also supports Java. For a zero-install solution, use our online tool above which generates Jackson-annotated POJOs, Lombok classes, or Records from any JSON you paste.

Try our free JSON to Java generator →

Handling Unknown Fields

APIs evolve over time and may add new fields. There are two strategies for handling unknown JSON fields in Jackson:

// Strategy 1: Ignore all unknown fields (recommended for API clients)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ApiResponse {
    private String id;
    private String status;
    // any other JSON fields are silently ignored
}

// Strategy 2: Capture unknown fields in a Map (useful for logging/debugging)
public class FlexibleResponse {

    private String id;
    private String status;

    // Collects all fields that don't map to any declared property
    private Map<String, Object> additionalProperties = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        additionalProperties.put(name, value);
    }
}

Enum Handling with Jackson

By default, Jackson serializes enums as their name string. Use@JsonValue and@JsonCreator for custom serialization logic:

public enum OrderStatus {
    PENDING("pending"),
    PROCESSING("processing"),
    SHIPPED("shipped"),
    DELIVERED("delivered"),
    CANCELLED("cancelled");

    private final String value;

    OrderStatus(String value) {
        this.value = value;
    }

    @JsonValue  // serializes as the value string, not the enum name
    public String getValue() { return value; }

    @JsonCreator  // deserializes from the value string
    public static OrderStatus fromValue(String value) {
        for (OrderStatus status : values()) {
            if (status.value.equalsIgnoreCase(value)) return status;
        }
        throw new IllegalArgumentException("Unknown status: " + value);
    }
}

// In your POJO:
public class Order {
    private String orderId;
    private OrderStatus status;  // serializes as "pending", not "PENDING"
}

Date and Time Handling: JavaTimeModule

Java 8+ date/time types (LocalDateTime,ZonedDateTime,Instant) require the jackson-datatype-jsr310 module:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;

// ObjectMapper setup
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

// POJO with various date types
public class Event {

    private String title;

    // ISO-8601 format: "2026-02-27T14:30:00"
    @JsonFormat(shape = JsonFormat.Shape.STRING,
                pattern = "yyyy-MM-dd'T'HH:mm:ss")
    private LocalDateTime startTime;

    // With timezone: "2026-02-27T14:30:00+09:00"
    @JsonFormat(shape = JsonFormat.Shape.STRING,
                pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
    private ZonedDateTime endTime;

    // Unix epoch in milliseconds: 1740614400000
    @JsonFormat(shape = JsonFormat.Shape.NUMBER)
    private Instant createdAt;
}

Inheritance and Polymorphic Deserialization

When JSON can represent multiple subtypes of a base class, use Jackson's@JsonTypeInfo and@JsonSubTypes to instruct the deserializer which concrete class to instantiate:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// JSON: {"type":"email","address":"alice@example.com","subject":"Hi"}
// JSON: {"type":"sms","phoneNumber":"+1234567890","message":"Hello"}

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,       // discriminator field holds a name
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"                 // the JSON key that selects the subtype
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = EmailNotification.class, name = "email"),
    @JsonSubTypes.Type(value = SmsNotification.class,   name = "sms"),
    @JsonSubTypes.Type(value = PushNotification.class,  name = "push"),
})
public abstract class Notification {
    public abstract String getType();
}

public class EmailNotification extends Notification {
    private String address;
    private String subject;
    public String getType() { return "email"; }
    // getters/setters
}

public class SmsNotification extends Notification {
    private String phoneNumber;
    private String message;
    public String getType() { return "sms"; }
    // getters/setters
}

// Deserialization automatically selects the right subclass
Notification n = mapper.readValue(json, Notification.class);
if (n instanceof EmailNotification email) {
    System.out.println(email.getAddress());
}

Validation with Bean Validation (JSR-380)

Jackson handles JSON mapping; Bean Validation (Hibernate Validator) handles data validation. Combine them in Spring Boot with@Valid on request bodies:

import jakarta.validation.constraints.*;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class CreateUserRequest {

    @NotBlank(message = "Username is required")
    @Size(min = 3, max = 50, message = "Username must be 3-50 characters")
    @JsonProperty("username")
    private String username;

    @NotBlank
    @Email(message = "Must be a valid email address")
    @JsonProperty("email")
    private String email;

    @NotNull
    @Min(value = 18, message = "Must be at least 18 years old")
    @Max(value = 150)
    @JsonProperty("age")
    private Integer age;

    @Size(min = 8, message = "Password must be at least 8 characters")
    @Pattern(regexp = ".*[A-Z].*", message = "Must contain an uppercase letter")
    @JsonProperty("password")
    private String password;
}

// Spring Boot REST controller
@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest req) {
        // Jackson deserializes JSON, then Spring validates with @Valid
        // Returns 400 Bad Request automatically if validation fails
        return ResponseEntity.ok(userService.create(req));
    }
}

Frequently Asked Questions

Q: What is a Java POJO and how does it differ from a Java Record?
A Plain Old Java Object (POJO) is a regular Java class with private fields, a no-arg constructor, and public getters/setters. It is mutable by default and works with all Java versions. A Java Record (introduced in Java 16 as stable) is an immutable data carrier declared with the record keyword. Records automatically generate a canonical constructor, accessor methods, equals(), hashCode(), and toString(). Use Records for immutable data transfer objects and POJOs when mutability or legacy framework compatibility is required.
Q: Which Jackson annotation maps a JSON field name to a differently named Java field?
@JsonProperty is the annotation you need. Place it on the field or its getter: @JsonProperty("first_name") private String firstName; This tells Jackson to map the JSON key "first_name" to the Java field firstName during both deserialization (JSON → Java) and serialization (Java → JSON). When the JSON key and Java field name are identical, you can omit the annotation.
Q: How do I deserialize a JSON array into a Java List with Jackson?
Use ObjectMapper.readValue() with a TypeReference to preserve generic type information at runtime: List<User> users = mapper.readValue(json, new TypeReference<List<User>>() {}); Without TypeReference, Java type erasure would lose the <User> parameter and Jackson would produce a List<LinkedHashMap> instead. For nested generics like List<Map<String, User>>, wrap the full type in the TypeReference.
Q: What does @JsonIgnoreProperties(ignoreUnknown = true) do?
By default, Jackson throws an UnrecognizedPropertyException when the JSON contains a field that has no corresponding Java field. Adding @JsonIgnoreProperties(ignoreUnknown = true) at the class level instructs Jackson to silently skip any unknown fields during deserialization. This is recommended in production APIs where the server may add new fields in the future without breaking existing clients.
Q: How do I use Lombok to reduce boilerplate in JSON-mapped Java classes?
Annotate your class with @Data (generates getters, setters, equals, hashCode, toString), @NoArgsConstructor (required by Jackson for deserialization), and @AllArgsConstructor. Add @Builder if you want a fluent builder pattern. Keep @JsonProperty on fields where the JSON key name differs from the Java field name. Make sure lombok is on the annotation processor path in Maven or Gradle.
Q: How do I handle null fields and optional values in Jackson?
Annotate your class with @JsonInclude(JsonInclude.Include.NON_NULL) to prevent null fields from appearing in serialized JSON output. During deserialization, missing JSON fields are assigned null for objects or default values for primitives. You can also use Optional<T> fields, but you must register the Jdk8Module: mapper.registerModule(new Jdk8Module()); Alternatively, use @JsonSetter(nulls = Nulls.SKIP) to skip null assignment.
Q: What is the difference between Jackson and Gson for JSON to Java conversion?
Jackson is the de-facto standard in Spring Boot and Spring Framework, offering superior performance, extensive configuration, and a rich annotation set. Gson, developed by Google, has a simpler API with @SerializedName for field mapping and uses TypeToken for generic types. Jackson handles more edge cases (polymorphism, property naming strategies, modules for Java 8 types and dates) but requires more dependency setup. Gson is a good choice for Android projects or simpler use cases.
Q: How do I handle Java 8 date types like LocalDateTime with Jackson?
Register the JavaTimeModule and disable WRITE_DATES_AS_TIMESTAMPS: ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Then annotate your date field: @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime createdAt; This serializes the date as an ISO-8601 string rather than a numeric timestamp.

Ready to convert your JSON to Java?

Paste any JSON and get Jackson-annotated POJOs, Lombok data classes, or Java Records instantly.

Open JSON to Java Tool →
𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

JVJSON to Java Class{ }JSON FormatterTSJSON to TypeScriptPYJSON to Python

Related Articles

JSON to TypeScript 온라인 가이드: 개발자 완벽 매뉴얼

JSON에서 TypeScript 타입을 자동 생성하는 방법. interface vs type, 선택적/nullable 필드, 중첩 객체, 유니온 타입, Zod 런타임 검증, 제네릭 API 응답 타입, tsconfig 모범 사례.

JSON to Python 데이터클래스: Pydantic, dataclasses, TypedDict, attrs 가이드

JSON을 Python 데이터클래스로 온라인 변환. Pydantic v2, dataclasses, attrs를 사용한 JSON 파싱 방법을 알아보세요.