- Сериализация в JSON
- Using Jackson for JSON Serialization and Deserialization
- 2. Converting a POJO to JSON
- 3. Pretty Printing
- 4. Ignore NULL fields
- 4.1. Configuring ObjectMapper
- 4.2. With an Annotation
- 5. Ignore Empty Arrays
- 6. Generate JSON String
- 7. Formatting Dates
- 8. Converting JSON to POJO
- 9. Adjusting Date Format
- 10. Ignoring Unknown Properties
- 11. Serializing Array of Objects to JSON
- 12. Deserialize Array of Objects from JSON
- Summary
Сериализация в JSON
— Раз уж ты познакомился с JSON, давай поговорим о нем сегодня подробнее.
— Ок. А где обычно он используется?
— Обычно дело выглядит так. Кто-то (клиент) запрашивает у Java-программы (сервера) данные. Программа создает Java-объекты и заполняет их информацией из базы данных. Затем преобразовывает их в формат понятный запрашивающему (клиенту), например JSON, и отсылает их обратно.
Давай я тебе расскажу, как работать с ним из Java. Собственно, нам понадобятся только две вещи – сериализовать Java-объекты в JSON-формат и десериализовать Java-объекты из формата JSON.
Т.е. JSON – это стандарт транспортировки сообщений/данных от одной программы к другой. Таких стандартов довольно много. Но если программа написана на JavaScript, она обычно старается работать с JSON.
Как ты уже знаешь, в Java есть встроенные стандартные средства сериализации. Но JSON к ним не относится. Поэтому если тебе надо использовать сериализацию объекта в JSON, ты можешь использовать один из популярных фреймворков(библиотек), которые это умеют.
— А чем отличаются различные фреймворки?
— Обычно они отличаются степенью сложности: есть фреймворки, которые умеют делать только самое необходимое, но они очень маленькие и простые. А есть и большие сложные фреймворки, которые могут делать гораздо больше.
Одним из популярных фреймворков считается Jackson. Мы рассмотрим работу с JSON на его примере.
Для начала тебе надо скачать этот фреймворк и добавить его себе в проект. Делать это надо в Intellij IDEA само собой. Загрузить фреймворк можно по ссылке.
Сконвертировать Java-объект в JSON примерно так же просто, как и сериализовать его. Для этого есть специальный класс ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper).
Давай я покажу тебе рабочий пример, а потом мы его разберем:
public static void main(String[] args) throws IOException < //создание объекта для сериализации в JSON Cat cat = new Cat(); cat.name = "Murka"; cat.age = 5; cat.weight = 4; //писать результат сериализации будем во Writer(StringWriter) StringWriter writer = new StringWriter(); //это объект Jackson, который выполняет сериализацию ObjectMapper mapper = new ObjectMapper(); // сама сериализация: 1-куда, 2-что mapper.writeValue(writer, cat); //преобразовываем все записанное во StringWriter в строку String result = writer.toString(); System.out.println(result); >
В строках 4-7 мы создаем объект класса Cat и заполняем его данными.
Строка 10 – создаем объект Writer, куда будем писать строку — JSON представление объекта.
Строка 13 – создаем объект ObjectMapper , который и выполняет всю сериализацию.
Строка 16 – пишем JSON-представление объекта cat в writer .
Строки 19-20 – выводим результат на экран.
Все выглядит довольно просто. Не сложнее родной сериализации в Java.
— А как будет выглядеть десериализация?
— Да почти так же, только короче:
public static void main(String[] args) throws IOException < String jsonString text-viola">"; StringReader reader = new StringReader(jsonString); ObjectMapper mapper = new ObjectMapper(); Cat cat = mapper.readValue(reader, Cat.class); >
Тут еще проще. Берем ObjectMapper и передаем в него строку с JSON или StringReader, а также класс объекта, который надо десериализовать . Вызываем метод readValue, и на выходе получаем готовый Java-объект со всеми данными.
— Ну, точно, как десериализация в Java.
— Почти. К объектам, которые сериализуются/десериализуются в JSON есть несколько требований:
1) поля должны быть видимые: или public или иметь getter’ы и setter’ы;
2) должен быть конструктор по умолчанию (без параметров).
— Ясно. Ожидаемо, в общем. Хотя Java отлично сериализовала и private поля.
— Так то — Java. У нее есть доступ к скрытым данным. От себя не утаишь.
Тут есть еще третий аспект. Надеюсь, ты обратил внимание на аннотацию @JsonAutoDetect в классе Cat?
— Ага. Как раз хотел спросить – что это такое.
— Это аннотации – служебная информация для фреймворка Jackson. Можно очень гибко управлять результатом сериализации в JSON формат, расставляя правильные аннотации.
— Круто! А что за аннотации есть?
Аннотация | Описание |
---|---|
@JsonAutoDetect | Ставится перед классом. Помечает класс как готовый к сериализациив JSON. |
@JsonIgnore | Ставится перед свойством. Свойство игнорируется при сериализации. |
@JsonProperty | Ставится перед свойством или getter’ом или setter’ом. Позволяет задать другое имя поля при сериализации. |
@JsonPropertyOrder | Ставится перед классом. Позволяет задать порядок полей для сериализации. |
— Есть много. Но не сейчас. Сейчас давай немного переделаем наш первый пример:
public static void main(String[] args) throws IOException < Cat cat = new Cat(); cat.name = "Murka"; cat.age = 5; cat.weight = 4; StringWriter writer = new StringWriter(); ObjectMapper mapper = new ObjectMapper(); mapper.writeValue(writer, cat); String result = writer.toString(); System.out.println(result); >
Код остался тот же, но я поменяла аннотации: указала другое имя полю name — имя alias. А также отметила поле weight как Ignore, в результате JSON объекта поменялся.
— Хорошо, что можно так всего настраивать – думаю, мне это обязательно пригодится.
А десериализация поймет, как с этим работать? При десериализации из JSON в Java-объект, значение поля alias будет занесено в name объекта Cat?
— Да, десериализация будет работать как надо. Она умная.
Спасибо за такую интересную лекцию, Элли.
Using Jackson for JSON Serialization and Deserialization
Jackson is one of the most common Java libraries for processing JSON. It is used for reading and writing JSON among other tasks. Using Jackson, you can easily handle automatic conversion from Java objects to JSON and back. In this article, we delve into some common Jackson usage patterns.
2. Converting a POJO to JSON
Suppose we want to convert a sample POJO (Plain Old Java Object) to JSON. An example class is defined below.
public class User < private String firstName; private String lastName; private Date dateOfBirth; private ListemailAddrs; public User(String firstName,String lastName) < this.firstName = firstName; this.lastName = lastName; >// standard getters and setters here >
Converting such an object to JSON is just a couple of lines:
ObjectMapper mapper = new ObjectMapper(); User user = new User(«Harrison», «Ford»); user.setEmailAddrs(Arrays.asList(«harrison@example.com»)); mapper.writeValue(System.out, user); // prints:
3. Pretty Printing
Note that the default output is quite compact. Sometimes it is useful to be able to view indented output for debugging purposes. To produce properly indented output, enable the option SerializationFeature.INDENT_OUTPUT on the ObjectMapper instance before using it to serialize the object.
ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); User user = new User("Harrison", "Ford"); .
The output generated now is nicely indented:
4. Ignore NULL fields
How do we tell Jackson not to serialize null values (as for “ dateOfBirth ” above)? There are a couple of ways. You can tell the ObjectMapper to skip all NULL fields, or you can use annotations to specify per class or per field.
4.1. Configuring ObjectMapper
. mapper.setSerializationInclusion(Include.NON_NULL); User user = new User(«Harrison», «Ford»); . // prints:
4.2. With an Annotation
Use the annotation @JsonInclude(Include.NON_NULL) on the target object class to eliminate serialization of all NULL fields.
@JsonInclude(Include.NON_NULL) public class User
Or on a field (or property) to disable specific NULL fields from being serialized. In the code below, dateOfBirth will be ignored if null, but not heightInM .
. @JsonInclude(Include.NON_NULL) private Date dateOfBirth; private Float heightInM; .
5. Ignore Empty Arrays
When you have a class with an empty array initializer, the value is serialized as an empty JSON array.
class User < . private ListphoneNumbers = new ArrayList<>(); . > // serialized to:
When you want to skip empty array declarations being output, you can use the following.
mapper.setSerializationInclusion(Include.NON_EMPTY);
6. Generate JSON String
How can we generate a JSON string representation instead of writing it directly to a file or an OutputStream? Maybe you want to store the JSON string in a database. Use the writeValueAsString() method.
// set up user String jsonStr = mapper.writeValueAsString(user);
7. Formatting Dates
By default, Jackson prints Date fields as numeric timestamps as shown below:
Calendar c = Calendar.getInstance(); c.clear(); c.set(1942, 6, 13); user.setDateOfBirth(c.getTime()); // prints:
Turning off numeric timestamp results in serialization of a date in the ISO 8601 format as shown below:
. mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); . // prints:
Change the default date format by using SimpleDateFormat as shown:
. DateFormat fmt = new SimpleDateFormat(«dd-MMM-yyyy»); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); . // prints:
8. Converting JSON to POJO
Reading and converting JSON to a Java object is just as easy with Jackson. Accomplished in just a couple of lines:
ObjectMapper mapper = new ObjectMapper(); User user = mapper.readValue(new File(jsonFile), User.class);
The target class (User in this case) needs a no-arguments default constructor defined – failing which you get this exception:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of sample.User: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
The situation is easily address by adding a no-arguments default constructor.
public class User < private String firstName; private String lastName; private Date dateOfBirth; private ListemailAddrs; public User() <> . >
9. Adjusting Date Format
As before, the date format needs to be adjusted unless it is in ISO 8601 format. Parsing the following JSON fails:
The following exception is reported:
Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not deserialize value of type java.util.Date from String "13-Jul-1942": not a valid representation (error: Failed to parse Date value '13-Jul-1942': Can not parse date "13-Jul-1942": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
Specify the date format if you are using a non-standard format as shown:
DateFormat fmt = new SimpleDateFormat("dd-MMM-yyyy"); mapper.setDateFormat(fmt);
10. Ignoring Unknown Properties
Sometimes the JSON you are trying to read might include some properties not defined in the Java class. Maybe the JSON was updated but the change is not yet reflected in the Java class. In such cases, you might end up with an exception like this:
Exception in thread "main" com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "gender" (class sample.User), not marked as ignorable (6 known properties: . )
You can tell Jackson to ignore such properties on a global level by disabling a deserialization feature as follows:
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Alternatively, you can use the following annotation on the class to ignore unknown properties.
@JsonIgnoreProperties(ignoreUnknown=true) public class User
11. Serializing Array of Objects to JSON
Serializing an array of objects to JSON is straightforward.
ObjectMapper mapper = new ObjectMapper(); List users = new ArrayList<>(); users.add(new User(. )); users.add(new User(. )); System.out.println(mapper.writeValueAsString(users));
The array is properly serialized as shown:
12. Deserialize Array of Objects from JSON
Several methods are available to deserialize a JSON array to a collection of Java objects. Use whatever method suits you depending on your requirements.
Deserializing a JSON array to a Java array of objects of a particular class is as simple as:
User[] users = mapper.readValue(new File(jsonFile), User[].class); System.out.println(mapper.writeValueAsString(users));
Using a JavaType is useful when constructing collections or parametric types.
JavaType listType = mapper .getTypeFactory() .constructCollectionType(List.class, User.class); List users = mapper.readValue(new File(jsonFile), listType); System.out.println(mapper.writeValueAsString(users));
A third method is to create and use a TypeReference.
TypeReference ref = new TypeReference>()<>; List users = mapper.readValue(new File(jsonFile), ref); System.out.println(mapper.writeValueAsString(users));
Summary
This article showed you how to convert Java objects to JSON and back. We also covered a few common use cases.