JSON (JavaScript Object Notation) — это текстовый формат обмена данными, который прост, понятен и легко читается как человеком, так и машиной. Он основан на подмножестве языка JavaScript, но не зависит от языка программирования, что делает его идеальным для передачи данных между сервером и клиентом в веб-приложениях. Благодаря своей простоте, JSON стал чрезвычайно популярным форматом для хранения и передачи данных в Интернете, заменив XML.
В современных Java-приложениях JSON широко используется для сериализации и десериализации данных, то есть для преобразования объектов в формат JSON и наоборот. Это позволяет легко интегрировать Java-приложения с другими сервисами, обмениваться данными между различными компонентами системы, а также хранить данные в файлах или базах данных в удобном и компактном формате. JSON стал стандартом де-факто для API и веб-сервисов, что делает его незаменимым инструментом для любого Java-разработчика.
Для работы с JSON в Java существует несколько популярных библиотек, каждая из которых имеет свои особенности, преимущества и недостатки. Самыми известными из них являются Jackson, Gson и JSON-B которые мы сейчас и рассмотрим. Но сначала рекомендация по обучению на курсах программирования. Если хочешь стать профи в сфере разработки — тебе в FoxmindEd. Академия, которая реально обучает.
Jackson
Jackson предоставляет несколько основных классов и методов для работы с JSON, среди которых самым важным является класс ObjectMapper. Этот класс отвечает за преобразование JSON в Java-объекты и наоборот.
- ObjectMapper: Основной класс для парсинга JSON. Он обеспечивает все необходимые методы для преобразования между JSON и Java-объектами.
- readValue: Метод, используемый для десериализации JSON в Java-объект.
- writeValueAsString: Метод, используемый для сериализации Java-объекта в JSON.
Рассмотрим простой пример, где мы парсим JSON в Java-объект и обратно сериализуем этот объект в JSON. Сначала нам нужно создать класс, который будет отображать JSON, который мы хотим распарсить.
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private int age;
private String email;
}
Примечание: не забудьте добавить к классу User конструктор без параметров иначе в методе readValue получите ошибку — InvalidDefinitionException.
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {
public static void main(String[] args) {
try {
// JSON строка
String jsonString = "{\"id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
ObjectMapper objectMapper = new ObjectMapper();
// Парсим JSON в Java-объект
User user = objectMapper.readValue(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Ім'я: " + user.getName());
System.out.println("Вік: " + user.getAge());
System.out.println("Email: " + user.getEmail());
// Сериализуем Java-объект в JSON
String jsonOutput = objectMapper.writeValueAsString(user);
System.out.println("Серіалізований JSON: " + jsonOutput);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Интересно, что будет, а если мы не хотим десеризовать определенные поля JSON? Давайте добавим еще одно поле в JSON, но класс User останется без изменений.
//Добавили 'phone' в JSON
String jsonString = "{\"id\":1,\"name\":\"John Doe\"," +
"\"age\":30,\"email\":\"johndoe@example.com\",\"phone\":\"+1234567890\"}";
После запуска кода мы получим ошибку UnrecognizedPropertyException. Ведь поля phone нет в классе User и ObjectMapper не знает, что с этим полем делать.
Чтобы этого избежать можно настроить ObjectMapper таким образом, чтобы он игнорировал неизвестные поля:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Или достаточно добавить аннотацию над классом User:
@JsonIgnoreProperties(ignoreUnknown = true)
Gson
Gson предоставляет простые в использовании методы для сериализации и десериализации данных:
- Gson: Основной класс, который отвечает за сериализацию и десериализацию JSON.
- fromJson: Метод для преобразования JSON в Java-объект. Он принимает JSON-строку и класс, в который этот JSON должен быть преобразован.
- toJson: Метод для преобразования Java-объекта в JSON-строку.
Важно отметить, что в отличие от ObjectMapper из библиотеки Jackson, который по умолчанию выбрасывает исключение при наличии неизвестных полей в JSON, Gson по умолчанию игнорирует такие поля и не вызывает ошибок. Это означает, что если в JSON присутствует поле, которого нет в соответствующем Java-классе, Gson просто пропустит это поле без каких-либо последствий. Такой подход делает Gson более гибким и устойчивым к изменениям структуры JSON, но также может привести к тому, что вы не заметите ошибок в данных, если какие-то поля неожиданно изменятся или будут добавлены.
Класс User оставляем без изменений. Только замечу, что для Gson не нужно добавлять в класс дефолтный (пустой) конструктор, как мы делали это с ObjectMapper.
import com.google.gson.Gson;
public class Main {
public static void main(String[] args) {
String jsonString = "{\"id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
Gson gson = new Gson();
User user = gson.fromJson(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Имя: " + user.getName());
System.out.println("Возраст: " + user.getAge());
System.out.println("Email: " + user.getEmail());
String jsonOutput = gson.toJson(user);
System.out.println("Сериализованный JSON: " + jsonOutput);
}
}
Gson позволяет настраивать процесс парсинга с помощью аннотаций. Самые распространенные аннотации:
- @SerializedName: Используется для указания альтернативного имени поля в JSON.
- @Expose: Используется для контроля сериализации и десериализации полей. Например, если использовать @Expose(serialize = false), то поле будет исключено из сериализации, но останется доступным для десериализации. Это может быть полезно, когда некоторые данные нужно скрыть при сериализации, но при этом их необходимо извлекать из JSON при десериализации.
Добавим новые аннотации к классу User
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
public class User {
@SerializedName("user_id")
@Expose
private Integer id;
@Expose(serialize = false)
private String name;
@Expose
private int age;
@Expose
private String email;
}
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class Main {
public static void main(String[] args) {
// JSON строка
String jsonString = "{\"user_id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
// Создание экземпляра Gson с учетом @Expose
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
User user = gson.fromJson(jsonString, User.class);
System.out.println("Іd: " + user.getId());
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
System.out.println("Email: " + user.getEmail());
// Сериализируем Java-объект у JSON
String jsonOutput = gson.toJson(user);
System.out.println("Сериализованный JSON: " + jsonOutput); // не будет содержать поля name
}
}
JSON-B
JSON-B (Java API for JSON Binding) — это официальный стандарт Java для сериализации и десериализации JSON в Java-объекты и наоборот. JSON-B входит в состав Java EE (Enterprise Edition) и является частью спецификации Jakarta EE, что делает его естественным выбором для корпоративных Java-приложений, особенно тех, которые используют Java EE или Jakarta EE. JSON-B предоставляет простой и унифицированный API для работы с JSON, что делает его удобным для использования в крупных проектах с четкими стандартами.
Для работы с JSON-B используется несколько основных классов и методов:
- Jsonb: Это основной интерфейс, который предоставляет методы для сериализации и десериализации объектов.
- JsonbBuilder: Класс для создания экземпляра Jsonb.
- fromJson: Метод для десериализации JSON-строки в Java-объект.
- toJson: Метод для сериализации Java-объекта в JSON-строку.
import jakarta.json.bind.annotation.JsonbProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
@JsonbProperty("user_id")
private Integer id;
private String name;
private int age;
private String email;
}
import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
public class Main {
public static void main(String[] args) {
String jsonString = "{\"user_id\":1,\"name\":\"John Doe\",\"age\":30,\"email\":\"johndoe@example.com\"}";
Jsonb jsonb = JsonbBuilder.create();
User user = jsonb.fromJson(jsonString, User.class);
System.out.println("Id: " + user.getId());
System.out.println("Name: " + user.getName());
System.out.println("Age: " + user.getAge());
System.out.println("Email: " + user.getEmail());
String jsonOutput = jsonb.toJson(user);
System.out.println("Сериализованный JSON: " + jsonOutput);
}
}
Дополнительно
Обработка сложных структур
До этого мы рассматривали пример, где JSON представлял собой простой объект с несколькими основными полями. Однако в реальных приложениях часто возникает необходимость работать с более сложными структурами, которые содержат массивы и вложенные объекты. Наш обновленный пример:
{
"id": 1,
"name": "John Doe",
"age": 30,
"email": "johndoe@example.com",
"phones": ["+1234567890", "+0987654321"],
"address": {
"street": "123 Main St",
"city": "Anytown"
},
"additionalInfo": {
"nickname": "JD",
"hobbies": "coding"
}
}
Примечание: поле additionalInfo в этом примере представляет собой динамически изменяемые данные. Это означает, что конкретные ключи, такие как «nickname» и «hobbies», могут изменяться или дополняться другими данными в разных сценариях. Поэтому вы не сможете просто создать статические поля класса для каждого возможного ключа.
Что ж, прежде всего, давайте обновим наш класс User в соответствии с новым JSON. Квадратные скобки после phones указывают на наличие массива, поэтому для хранения телефонов мы используем тип List<String>. Поле address в JSON является вложенным объектом, который содержит информацию об улице и городе. Для его отображения мы создадим отдельный класс Address, где каждое поле будет представлено соответствующим типом.
Что касается поля additionalInfo, это особый случай. В JSON оно представлено как объект, но его ключи и значения могут меняться динамически. Поскольку мы не знаем заранее, какие именно ключи могут присутствовать, мы используем Map<String, Object>. Это предоставляет нам гибкость для хранения произвольных пар «ключ-значение», которые могут меняться в зависимости от ситуации.
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private int age;
private String email;
private List<String> phones;
private Address address;
private Map<String, Object> additionalInfo;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public static class Address {
private String street;
private String city;
}
}
Для парсинга используем ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Парсим JSON в Java-объект
User user = objectMapper.readValue(jsonString, User.class);
// Сериализуем Java-объект в JSON
String jsonOutput = objectMapper.writeValueAsString(user);
System.out.println("Сериализованный JSON: " + jsonOutput);
Вывод
Парсинг JSON в Java является важной частью разработки современных приложений, где обмен данными между компонентами или сервисами осуществляется с помощью этого популярного формата. Важно понимать основные библиотеки для работы с JSON, такие как Jackson, Gson и JSON-B, каждая из которых имеет свои преимущества и особенности использования. Выбор библиотеки зависит от конкретных требований вашего проекта и удобства их использования.
Не менее важной является настройка поведения библиотеки в случае некорректного формата JSON. Это позволяет избежать неожиданных сбоев и обеспечить более надежную обработку данных. Использование встроенных механизмов для обработки ошибок или настройка библиотеки таким образом, чтобы она игнорировала неизвестные поля, может значительно повысить устойчивость приложения.
Подытоживая, парсинг JSON в Java — это не просто выбор библиотеки. Это также понимание того, как настроить ее под конкретные требования, обрабатывать ошибки и эффективно работать с большими объемами данных. Следуя этим принципам, вы сможете создавать надежные и эффективные приложения, которые будут легко справляться с любыми задачами, связанными с обработкой JSON.
🤔 Остались вопросы о парсинге JSON в Java? - смело задавайте ниже! 💬