TL;DR
JSON 转 Java 类工具可以从任意 JSON 数据生成带有 Jackson 注解的 POJO、Lombok 数据类或 Java Record,消除手动编码并防止类型错误。 立即使用免费的 JSON 转 Java 类工具 →
核心要点
- Use
@JsonPropertyto 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
TypeReferencewith Jackson orTypeTokenwith Gson to preserve generic type info. - Lombok
@Data+@NoArgsConstructorreduces boilerplate while staying Jackson-compatible. - Java Records (Java 16+) provide immutable POJOs with minimal code; add
@JsonCreatorfor custom deserialization. - Register
JavaTimeModuleto handleLocalDateTimeandZonedDateTimewithout 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
| Approach | Boilerplate | Mutable | Best For |
|---|---|---|---|
| Plain POJO | High | Yes | Legacy code, JPA entities |
| Lombok @Data | Low | Yes | Spring Boot services, DTOs |
| Java Record | Minimal | No | Immutable 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();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();
}@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));
}
}常见问题
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 →