- Spring Boot: преобразование объекта в json
- Изменение имени поля
- Сортировка полей в алфавитном порядке
- Исключение пустых полей
- Произвольное количество полей
- Программное преобразование в json
- Заключение
- How to manage JSON in Spring Boot with Jackson
- What is Jackson?
- Using Jackson in a Spring Boot project
- Using Jackson annotations
- Serializing Java objects to JSON
- Deserializing JSON to Java objects
- Conclusion
Spring Boot: преобразование объекта в json
Spring Boot значительно упрощает преобразование объекта в json. Предположим, у нас есть такой класс, содержащий какую-то информацию о пользователе:
public class User <
private String firstName;
private String lastName;
private int age;
// далее идут get- и set-методы для указанных полей.
>
Создадим контроллер, который будет его возвращать.
@RestController
public class JsonController
@GetMapping
public User getUser() var user = new User();
user.setFirstName( «Сигизмунд» );
user.setLastName( «Петров» );
user.setAge( 23 );
return user;
>
>
Этих двух классов достаточно, чтобы вы уже могли получать информацию о пользователе в формате json! Выполнив GET-запрос к вашему приложению (по умолчанию http://127.0.0.1:8080/), вы получите такой json:
То есть имена полей объекта мапятся один к одному в их представление в формате json.
Изменение имени поля
А что делать, если мы хотим поменять имя поля в json, не меняя при этом имя поля в java? Например, lastName переименовать в surname. На помощь нам придёт аннотация @JsonProperty, в качестве параметра мы указываем желаемое имя для данного поля.
private String firstName;
@JsonProperty ( «surname» )
private String lastName;
private int age;
В формате json наш объект будет выглядеть уже так:
Сортировка полей в алфавитном порядке
В нашем примере у нас всего 3 поля и их порядок в формате json соответствует таковому в исходном классе. А если полей будет 33, то при тестировании будет сложно искать глазами нужное поле по его имени. Чтобы упростить поиск, давайте отсортируем имена полей в алфавитном порядке:
@JsonPropertyOrder (alphabetic = true )
public class User
private String firstName;
@JsonProperty ( «surname» )
private String lastName;
private int age;
Тогда json примет такой вид:
Как видим, поля отсортировались в алфавитном порядке.
Исключение пустых полей
Теперь предположим, что значения для какого-то поля у нас нет. Например, фамилии:
@GetMapping
public User getUser() var user = new User();
user.setFirstName( «Сигизмунд» );
user.setLastName( null );
user.setAge( 23 );
return user;
>
Тогда наш json будет выглядеть следующим образом:
Что делать, если мы вообще не хотим отображать поля, для которых у нас нет значений? Мы можем либо явно исключить конкретное поле с помощью аннотации @JsonIgnore:
Либо с помощью аннотации @JsonInclude на уровне всего класса указать, что в json попадают только поля с not-null значениями. Данный вариант предпочтительнее, поскольку задаёт единообразное поведение всех полей объекта.
@JsonInclude (JsonInclude.Include. NON_NULL )
public class User private String firstName;
private String lastName;
private int age;
И в том, и в другом случае поле lastName будет исключено из json:
Произвольное количество полей
Если нам заранее не известно количество и типы полей, мы можем сложить их все в одну мапу, ключи которой при сериализации будут преобразованы в имена полей сущности, а значения мапы – в значения этой сущности. В этом нам поможет аннотация @JsonAnyGetter, которая вешается не на саму мапу, а на её get-метод.
private Map params = new TreeMap<>();
@JsonAnyGetter
public Map getParams() return params;
>
>
Тут мы используем TreeMap, ключи которой сортируются по алфавиту, т.к. аннотация @JsonPropertyOrder в данном случае не работает. Значения типизированы классом Object, чтобы в мапу можно было складывать объекты любых типов (и строки, и числа).
Заполнение полей может выглядеть так:
@GetMapping
public User getUser() var user = new User();
user.getParams().put( «firstName» , «Сигизмунд» );
user.getParams().put( «surname» , «Петров» );
user.getParams().put( «age» , 23 );
return user;
>
В результате мы получим уже знакомый нам формат json:
Как видим, аннотация @JsonAnyGetter с одной стороны придаёт некоторую гибкость, но при этом не все рассмотренные выше аннотации с ней работают. В общем случае, если набор параметров фиксирован и заранее известен, лучше данный подход не использовать.
Программное преобразование в json
Кроме сериализации ответа на запрос, преобразование в json вы также можете выполнять в явном виде. Это бывает полезно, если json-представление объекта нужно записать в базу данных или в лог. Для этого нужно лишь внедрить в наш контроллер экземпляр класса ObjectMapper и использовать его метод writeValuesAsString().
@RestController
public class JsonController
private static final Logger log = LoggerFactory.getLogger(JsonController. class );
private final ObjectMapper mapper;
public JsonController(ObjectMapper mapper) this .mapper = mapper;
>
@GetMapping
public User getUser() throws JsonProcessingException var user = new User();
user.setFirstName( «Сигизмунд» );
user.setAge( 23 );
log.info( «Json: <>» , mapper.writeValueAsString(user));
return user;
>
>
Обратите внимание, что у нас один конструктор в контроллере, поэтому аннотацию @Autowired перед конструктором мы можем опустить. Spring Boot видит зависимость от ObjectMapper и подгружает в контроллер его экземпляр. При выполнении запроса вы увидите json-представление класса User в логе приложения.
Заключение
Как видите, Spring Boot позволяет легко преобразовывать объекты в json, а также изменять дефолтное поведение сериализации при помощи аннотаций.
How to manage JSON in Spring Boot with Jackson
In this tutorial we will learn how to produce and consume JSON data in a Spring Boot applications using the built-in Jackson provider.
What is Jackson?
Jackson is a Java library for handling JSON data. It provides a set of tools for parsing, generating, and manipulating JSON data in Java applications. Jackson is widely used in the Java ecosystem, including in popular frameworks like Spring Boot.
Using Jackson in a Spring Boot project
Let’s see an example application which uses Jackson to manipulate some data. Consider the following Spring Boot Controller class:
@RestController public class CustomerController < @Autowired CustomerRepository repository; @RequestMapping("/list") public ListfindAll() < return repository.getData(); >@RequestMapping("/one/") public Customer findOne(@PathVariable int id) < return repository.getData().get(id); >>
Assumed that the Customer object is created in the following way:
@Component public class CustomerRepository < List < Customer >customerList = new ArrayList < Customer >(); @PostConstruct public void init() < try < customerList.add(new Customer(1, "frank"); customerList.add(new Customer(2, "john"); >catch (ParseException e) < e.printStackTrace(); >> public List < Customer >getData() < return customerList; >>
Then, when requesting the URL http://localhost:8080/list the following JSON data will be returned:
There is no need to add any jackson-core,jackson-annotations and jackson-databind as their jars are automatically added by Spring Boot:
jar tvf demo-rest-0.0.1-SNAPSHOT.jar . . . . 1348389 Tue Aug 06 01:41:54 CEST 2019 BOOT-INF/lib/jackson-databind-2.9.9.3.jar 66519 Sat Jul 29 20:53:26 CEST 2017 BOOT-INF/lib/jackson-annotations-2.9.0.jar 325632 Wed May 15 19:58:38 CEST 2019 BOOT-INF/lib/jackson-core-2.9.9.jar 33429 Thu May 16 03:26:48 CEST 2019 BOOT-INF/lib/jackson-datatype-jdk8-2.9.9.jar 100674 Thu May 16 03:27:12 CEST 2019 BOOT-INF/lib/jackson-datatype-jsr310-2.9.9.jar 8645 Thu May 16 03:26:28 CEST 2019 BOOT-INF/lib/jackson-module-parameter-names-2.9.9.jar
Using Jackson annotations
In the first example, we were using the efault Jackson serialization/deserialization. Therefore there was no need to add any extra dependency. On the other hand, if you want to use Jackson annotations in your code, to compile the application it is required to add a Jackson dependency.
Let’s create a more complex Customer object:
package com.example.testrest; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Date; public class Customer < @JsonIgnore private int id; @JsonProperty("customer_name") private String name; @JsonSerialize(using = CustomDateSerializer.class) public Date birthDate; public Customer(int id, String name, Date birthDate) < this.id = id; this.name = name; this.birthDate = birthDate; >// Getter/Setter omitted for brevity >
This class contains the following annotations:
- @com.fasterxml.jackson.annotation.JsonIgnore: This annotation indicates that the property is to be ignored by introspection-based serialization and deserialization functionality.
- @com.fasterxml.jackson.annotation.JsonProperty: This annotation can be used to provide a custom name field name in JSON objects
- @com.fasterxml.jackson.databind.annotation.JsonSerialize: This annotation can be used to provide a custom Java class to serialize the content of JSON object.
package com.example.testrest; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; public class CustomDateSerializer extends StdSerializer < private static SimpleDateFormat formatter = new SimpleDateFormat("EEEEE MMMMM yyyy"); public CustomDateSerializer() < this(null); >public CustomDateSerializer(Class t) < super(t); >@Override public void serialize( Date value, JsonGenerator gen, SerializerProvider arg2) throws IOException, JsonProcessingException < gen.writeString(formatter.format(value)); >>
In the new example, we will include the extra Date field will be included in the Constructor of the Customer class:
@PostConstruct public void init() < try < customerList.add(new Customer(1, "frank", new SimpleDateFormat("dd/MM/yyyy").parse("17/10/1970"))); customerList.add(new Customer(2, "john", new SimpleDateFormat("dd/MM/yyyy").parse("25/07/1980"))); >catch (ParseException e) < e.printStackTrace(); >>
The result of (http://localhost:8080/list), will be the following JSON data:
In order to compile your example, you will need to include the following extra dependency:
com.fasterxml.jackson.core jackson-core 2.14.2
Serializing and Deserializing Data
In our example, the RestController was managing the serialization of a Java class in a JSON Media Type. On the other hand, you can also perform serialization and deserialization from your code. Let’s see how.
Serializing Java objects to JSON
To serialize a Java object to JSON using Jackson, you can use the ObjectMapper class. The ObjectMapper class provides methods for serializing Java objects to JSON and deserializing JSON data to Java objects.
Here’s an example of how to serialize a Java object to JSON using Jackson:
import com.fasterxml.jackson.databind.ObjectMapper; public class MyObject < private String name; private int age; // getters and setters omitted for brevity public static void main(String[] args) throws Exception < MyObject obj = new MyObject(); obj.setName("John"); obj.setAge(30); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(obj); System.out.println(json); >>
In this example, we’re creating a MyObject instance, setting its properties, and then using an ObjectMapper instance to serialize the object to a JSON string. The writeValueAsString() method of the ObjectMapper class converts the Java object to a JSON string.
Deserializing JSON to Java objects
To deserialize JSON data to a Java object using Jackson, you can again use the ObjectMapper class. The ObjectMapper class provides methods for deserializing JSON data to Java objects.
Here’s an example of how to deserialize a JSON string to a Java object using Jackson:
import com.fasterxml.jackson.databind.ObjectMapper; public class MyObject < private String name; private int age; // getters and setters omitted for brevity public static void main(String[] args) throws Exception < String json = ""; ObjectMapper mapper = new ObjectMapper(); MyObject obj = mapper.readValue(json, MyObject.class); System.out.println(obj.getName()); System.out.println(obj.getAge()); > >
In this example, we’re creating a String variable containing a JSON string, and then using an ObjectMapper instance to deserialize the JSON string to a MyObject instance. The readValue() method of the ObjectMapper class converts the JSON data to a Java object.
Conclusion
Jackson is a powerful library for working with JSON data in Java applications. In this tutorial, we’ve seen how to use Jackson in a Spring Boot project to serialize Java objects to JSON and deserialize JSON data to Java objects. With this knowledge, you should be able to work with JSON data in your Spring Boot applications using Jackson.