JSON to Java
Генерація класів Java POJO з JSON
Введення JSON
Виведення Java
Що таке конвертація JSON у класи Java?
Конвертація JSON у класи Java бере сирий JSON-об'єкт і генерує визначення Plain Old Java Object (POJO) з приватними полями, гетерами та сеттерами. Java не має вбудованої системи типів для JSON, тому кожна відповідь API, конфігураційний файл або повідомлення потребують відповідного класу, перш ніж ви зможете безпечно працювати з ними в типізований спосіб. Бібліотеки Jackson і Gson відображають JSON-ключі на поля Java через рефлексію, але спочатку потрібні визначення класів.
Стандартний Java POJO для десеріалізації JSON оголошує приватне поле для кожного JSON-ключа, конструктор без аргументів та пару гетер/сеттер для кожного поля. Вкладені JSON-об'єкти стають окремими класами. Масиви перетворюються на поля List<T> з імпортом java.util.List. Примітивні JSON-типи відображаються на примітиви Java (int, double, boolean) або їх типи-обгортки (Integer, Double, Boolean) при використанні в узагальнених типах. Значення null зазвичай відображаються на Object або посилальний тип, що допускає null.
Написання таких визначень класів вручну є монотонним. Ви читаєте кожен JSON-ключ, визначаєте тип Java за значенням, перетворюєте конвенції іменування з camelCase JSON на поля camelCase Java, створюєте назви класів у PascalCase для вкладених об'єктів і додаєте шаблонний код гетерів/сеттерів. Для JSON-об'єкта з 15 полями та 3 вкладеними об'єктами це означає написання 4 класів, понад 30 методів і підтримку узгодженості. Конвертер виконує це за мілісекунди.
Навіщо використовувати конвертер JSON to Java?
Ручне створення Java POJO з JSON означає перевірку кожного поля, визначення типів за зразками значень, написання пар гетер/сеттер та повторення цього процесу для кожного вкладеного об'єкта. Коли API-контракт змінюється, все доводиться оновлювати вручну. Конвертер усуває цю рутинну роботу.
Сценарії використання JSON to Java
Таблиця відповідності типів JSON та Java
Кожне JSON-значення відображається на конкретний тип Java. Таблиця нижче показує, як конвертер перекладає кожен JSON-тип у його Java-еквівалент. Стовпець «Альтернатива» показує типи-обгортки, що використовуються в узагальнених контекстах, на кшталт List<T>, або новіші можливості Java, як-от Records.
| Тип JSON | Приклад | Тип Java | Альтернатива |
|---|---|---|---|
| string | "hello" | String | String |
| number (integer) | 42 | int | int / Integer |
| number (float) | 3.14 | double | double / Double |
| boolean | true | boolean | boolean / Boolean |
| null | null | Object | Object (or @Nullable String) |
| object | {"k": "v"} | NestedClass | Record (Java 16+) |
| array of strings | ["a", "b"] | List<String> | List<String> |
| array of objects | [{"id": 1}] | List<Item> | List<Item> |
| mixed array | [1, "a"] | List<Object> | List<Object> |
Довідник анотацій Java для JSON
При десеріалізації JSON з Jackson або Gson анотації керують відображенням JSON-ключів на поля Java, обробкою невідомих полів і поведінкою null-значень. Цей довідник охоплює анотації, з якими ви найчастіше стикатиметеся при роботі зі згенерованими POJO.
| Анотація | Призначення | Бібліотека |
|---|---|---|
| @JsonProperty("name") | Maps a JSON key to a Java field with a different name | Jackson |
| @SerializedName("name") | Same as @JsonProperty, but for the Gson library | Gson |
| @JsonIgnoreProperties | Ignores unknown JSON keys during deserialization instead of failing | Jackson |
| @Nullable | Marks a field as accepting null values from JSON input | JSR 305 / JetBrains |
| @NotNull | Enforces that a field must not be null, throws on violation | Bean Validation |
| @JsonFormat(pattern=...) | Defines a date/time format for serialization and deserialization | Jackson |
POJO vs Record vs Lombok
У Java є три поширені підходи для визначення типізованих структур зберігання JSON-даних. Кожен підходить для різного стилю проекту та версії Java. POJO — традиційний патерн з найширшою сумісністю. Records зменшують шаблонний код для незмінних даних. Lombok генерує гетери, сеттери та конструктори під час компіляції через анотації.
Приклади коду
Ці приклади показують, як використовувати згенеровані Java POJO з Jackson для десеріалізації, як програмно генерувати Java-класи з JavaScript і Python, а також як використовувати CLI-інструмент jsonschema2pojo для пакетної генерації.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.List;
@JsonIgnoreProperties(ignoreUnknown = true)
public class User {
private int id;
private String name;
private String email;
private boolean active;
private double score;
private Address address;
private List<String> tags;
// getters and setters omitted for brevity
public static void main(String[] args) throws Exception {
String json = "{\"id\":1,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"active\":true,\"score\":98.5,\"address\":{\"street\":\"123 Main St\",\"city\":\"Springfield\",\"zip\":\"12345\"},\"tags\":[\"admin\",\"user\"]}";
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
System.out.println(user.getName()); // -> Alice
}
}
class Address {
private String street;
private String city;
private String zip;
// getters and setters
}// Minimal JSON-to-Java-POJO generator in JS
function jsonToJava(obj, name = "Root") {
const classes = [];
function infer(val, fieldName) {
if (val === null) return "Object";
if (typeof val === "string") return "String";
if (typeof val === "number") return Number.isInteger(val) ? "int" : "double";
if (typeof val === "boolean") return "boolean";
if (Array.isArray(val)) {
const first = val.find(v => v !== null);
if (!first) return "List<Object>";
const elem = infer(first, fieldName);
const boxed = elem === "int" ? "Integer" : elem === "double" ? "Double" : elem === "boolean" ? "Boolean" : elem;
return `List<${boxed}>`;
}
if (typeof val === "object") {
const cls = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
build(val, cls);
return cls;
}
return "Object";
}
function build(obj, cls) {
const fields = Object.entries(obj).map(([k, v]) => {
const type = infer(v, k);
return ` private ${type} ${k};`;
});
classes.push(`public class ${cls} {\n${fields.join("\n")}\n}`);
}
build(obj, name);
return classes.join("\n\n");
}
console.log(jsonToJava({ id: 1, name: "Alice", scores: [98, 85] }, "User"));
// public class User {
// private int id;
// private String name;
// private List<Integer> scores;
// }import json
def json_to_java(obj: dict, class_name: str = "Root") -> str:
classes = []
def infer(val, name):
if val is None:
return "Object"
if isinstance(val, bool):
return "boolean"
if isinstance(val, int):
return "int"
if isinstance(val, float):
return "double"
if isinstance(val, str):
return "String"
if isinstance(val, list):
if not val:
return "List<Object>"
elem = infer(val[0], name)
boxed = {"int": "Integer", "double": "Double", "boolean": "Boolean"}.get(elem, elem)
return f"List<{boxed}>"
if isinstance(val, dict):
cls = name[0].upper() + name[1:]
build(val, cls)
return cls
return "Object"
def build(obj, cls):
fields = [f" private {infer(v, k)} {k};" for k, v in obj.items()]
classes.append(f"public class {cls} {{\n" + "\n".join(fields) + "\n}")
build(obj, class_name)
return "\n\n".join(classes)
data = json.loads('{"id": 1, "name": "Alice", "tags": ["admin"]}')
print(json_to_java(data, "User"))
# public class User {
# private int id;
# private String name;
# private List<String> tags;
# }# Install jsonschema2pojo (requires Java 8+)
# Download from https://github.com/joelittlejohn/jsonschema2pojo
# Generate POJOs from a JSON file
jsonschema2pojo --source user.json --target src/main/java \
--source-type json --annotation-style jackson2
# Generate with Gson annotations instead
jsonschema2pojo --source user.json --target src/main/java \
--source-type json --annotation-style gson
# From a JSON string via stdin
echo '{"id": 1, "name": "Alice", "tags": ["admin"]}' | \
jsonschema2pojo --source-type json --annotation-style jackson2 \
--target src/main/java --target-package com.example.model