Статьи Генератор паролей UUID MD5 Unix-время URL-encode Base64 Форматирование XML Ваш внешний IP Число прописью


Spring Boot: преобразование объекта в json

Вернуться назад

27 января 2020

Тэги: Spring Boot rest Java 10

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:

{
  "firstName": "Сигизмунд",
  "lastName": "Петров",
  "age": 23
}

То есть имена полей объекта мапятся один к одному в их представление в формате json.

Изменение имени поля

А что делать, если мы хотим поменять имя поля в json, не меняя при этом имя поля в java? Например, lastName переименовать в surname. На помощь нам придёт аннотация @JsonProperty, в качестве параметра мы указываем желаемое имя для данного поля.

public class User {

    private String firstName;
    @JsonProperty("surname")
    private String lastName;
    private int age;

В формате json наш объект будет выглядеть уже так:

{
  "firstName": "Сигизмунд",
  "age": 23,
  "surname": "Петров"
}

Сортировка полей в алфавитном порядке

В нашем примере у нас всего 3 поля и их порядок в формате json соответствует таковому в исходном классе. А если полей будет 33, то при тестировании будет сложно искать глазами нужное поле по его имени. Чтобы упростить поиск, давайте отсортируем имена полей в алфавитном порядке:

@JsonPropertyOrder(alphabetic = true)
public class User {

    private String firstName;
    @JsonProperty("surname")
    private String lastName;
    private int age;

Тогда json примет такой вид:

{
  "age": 23,
  "firstName": "Сигизмунд",
  "surname": "Петров"
}

Как видим, поля отсортировались в алфавитном порядке.

Исключение пустых полей

Теперь предположим, что значения для какого-то поля у нас нет. Например, фамилии:

@GetMapping
public User getUser() {
    var user = new User();
    user.setFirstName("Сигизмунд");
    user.setLastName(null);
    user.setAge(23);
    return user;
}

Тогда наш json будет выглядеть следующим образом:

{
  "age": 23,
  "firstName": "Сигизмунд",
  "surname": null
}

Что делать, если мы вообще не хотим отображать поля, для которых у нас нет значений? Мы можем либо явно исключить конкретное поле с помощью аннотации @JsonIgnore:

public class User {
    private String firstName;
    @JsonIgnore
    private String lastName;
    private int age;

Либо с помощью аннотации @JsonInclude на уровне всего класса указать, что в json попадают только поля с not-null значениями. Данный вариант предпочтительнее, поскольку задаёт единообразное поведение всех полей объекта.

@JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
    private String firstName;
    private String lastName;
    private int age;

И в том, и в другом случае поле lastName будет исключено из json:

{
  "age": 23,
  "firstName": "Сигизмунд"
}

Произвольное количество полей

Если нам заранее не известно количество и типы полей, мы можем сложить их все в одну мапу, ключи которой при сериализации будут преобразованы в имена полей сущности, а значения мапы - в значения этой сущности. В этом нам поможет аннотация @JsonAnyGetter, которая вешается не на саму мапу, а на её get-метод.

public class User {

    private Map<String, Object> params = new TreeMap<>();

    @JsonAnyGetter
    public Map<String, Object> 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:

{
  "age": 23,
  "firstName": "Сигизмунд",
  "surname": "Петров"
}

Как видим, аннотация @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 в логе приложения.

r.d.example.controller.JsonController    : Json: {"firstName":"Сигизмунд","age":23}

Заключение

Как видите, Spring Boot позволяет легко преобразовывать объекты в json, а также изменять дефолтное поведение сериализации при помощи аннотаций.