В данной заметке я расскажу, как в spring data rest сериализовать в json сложный объект в виде строки. Советы справедливы для spring-data-rest версии 2.5.4. Пусть есть класс Topic с коллекцией сущностей Record, а у класса Record есть поле:
java.time.LocalDate releaseDate; |
При обслуживании HTTP запросов к единичному объекту (например, http://localhost:8080/server/records/5 ) сериализации можно добиться аннотациями:
@JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) @Column(name = "release_date") LocalDate releaseDate; |
При получении же списка ( http://localhost:8080/server/records или http://localhost:8080/server/topics/1/recordList/ ) вы получите вместо строки вида «2016-12-01» полноценный, но совершенно ненужный массив свойств:
releaseDate": { "year": 2016, "month": "DECEMBER", "dayOfMonth": 1, "monthValue": 12, "dayOfWeek": "THURSDAY", "era": "CE", "dayOfYear": 336, "leapYear": true, "chronology": { "id": "ISO", "calendarType": "iso8601" } } |
Я испробовал много всего, к примеру навешивал аннотации на getter:
@JsonSerialize(using = LocalDateSerializer.class) @JsonFormat("yyyy-MM-dd") public LocalDate getReleaseDate() { return releaseDate; } |
Но по всему выходило, что аннотации спрингом игнорируются.
Решение нашел тут: дельная ссылка
Можно изменить способ сериализации для типа во всех классах сразу, при этом на тип больше не надо вешать аннотации. В пакете, который прошаривается аннотацией ComponentScan нужно создать класс:
package ru.outofrange.config; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; import ru.outofrange.model.util.JsonDateDeserializer; import ru.outofrange.model.util.JsonDateSerializer; import java.time.LocalDate; @Component public class ObjectMapperCustomizer implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (!(bean instanceof ObjectMapper)) { return bean; } ObjectMapper mapper = (ObjectMapper) bean; mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); registerSerializerDserializer(mapper); return mapper; } private void registerSerializerDserializer(ObjectMapper mapper) { SimpleModule module = new SimpleModule(); module.addSerializer(LocalDate.class, new JsonDateSerializer()); module.addDeserializer(LocalDate.class, new JsonDateDeserializer()); mapper.registerModule(module); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } } |
Как можно видеть по приведенной ссылке, таким образом можно настроить сериализацию любого количества кастомных типов.
На всякий случай код еще двух вспомогательных классов-аннотаций:
package ru.outofrange.model.util; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.node.TextNode; import java.io.IOException; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; public class JsonDateDeserializer extends JsonDeserializer <LocalDate> { @Override public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { ObjectCodec oc = jp.getCodec(); TextNode node = oc.readTree(jp); String dateString = node.textValue(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); LocalDate date = LocalDate.parse(dateString, formatter); return date; } } |
package ru.outofrange.model.util; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; public class JsonDateSerializer extends JsonSerializer <LocalDate> { private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); @Override public void serialize(LocalDate date, JsonGenerator generator, SerializerProvider provider) throws IOException { String dateString = date.format(formatter); generator.writeString(dateString); } } |