У меня есть этот класс:
public class MyLocalApplicationClass {
private String name
private LocalDateTime creationDate;
private String createdBy;
}
И в ответ, который я получаю от службы отдыха, я получаю этот объект (в виде json):
public class MyRemoteApplicationClass {
private String name
private Date creationDate;
private String createdBy;
}
Поэтому, когда я отправляю запрос, я получаю значение (json) createDate из MyRemoteApplicationClass следующим образом:
{
“name”:”anything”,
"creation_date": 1666190973000,
"created_by": “anyone”
}
Поэтому мне было интересно, можно ли получить это как LocalDateTime или я должен получить его как Date, а затем перевести его в LocalDateTime (думаю, это мой лучший вариант)?
Потому что я пытаюсь получить его как LocalDateTime, но выдает эту ошибку:
raw timestamp (1656015404000) not allowed for
java.time.LocalDateTime: need additional information such as an
offset or time-zone (see class Javadocs)
Кроме того, я пытался получить его только как LocalDate, но он выдает эту ошибку (которую я уже добавил зависимость от jsr310, а также добавил сериализатор и десериализатор с этими аннотациями @JsonSerialize(using = LocalDateSerializer.class) @JsonDeserialize(using = LocalDateDeserializer.class), поэтому я думаю, что настоящая проблема заключается в предупреждении) и предупреждение:
Java 8 date/time type `java.time.DateTimeException` not supported
by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
to enable handling (through reference chain:
com.fasterxml.jackson.databind.JsonMappingException["cause"])
WARN -- com.fasterxml.jackson.databind.JsonMappingException:
Invalid value for EpochDay (valid values -365243219162 - 365241780471): 1656015404000




Есть несколько возможных решений:
JavaTimeModule и применив требуемую точность отметки времени.Давайте пройдемся по этим вариантам.
Мы можем объявить конструктор всех аргументов внутри MyLocalApplicationClass и аннотировать каждый аргумент @JsonProperty. Параметр contranctior creationDate должен быть получен как long, так как он будет проанализирован от миллисекунды эпохи до LocalDateTime вручную.
Вот как это может выглядеть:
public class MyLocalApplicationClass {
private String name;
private LocalDateTime creationDate;
private String createdBy;
public MyLocalApplicationClass(@JsonProperty("name") String name,
@JsonProperty("creation_date") long creationDate,
@JsonProperty("created_by") String createdBy) {
this.name = name;
this.createdBy = createdBy;
this.creationDate = Instant
.ofEpochMilli(creationDate)
.atZone(ZoneOffset.UTC)
.toLocalDateTime();
}
}
Другим вариантом может быть применение глобальных конфигураций десериализации.
Но для этого потребуется изменить тип свойства creationDate на Instant.
Чтобы правильно настроить ObjectMapper, нам нужно сделать два шага:
JavaTimeModule модуль;ObjectMapper о точности метки времени, установив для свойства десериализации DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS значение false.Для этого мы можем поместить Jackson2ObjectMapperBuilder и ObjectMapper как бобы в контекст Spring.
@Configuration
public class JsonConfig {
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder();
}
@Bean
public ObjectMapper objectMapper() {
return jackson2ObjectMapperBuilder()
.build()
.registerModule(new JavaTimeModule())
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
}
}
Обратите внимание, что creationDate должен быть типа Instant, аннотации привязки данных в этом случае нужны только потому, что имена свойств и полей не совпадают (они не связаны с преобразованием).
public class MyLocalApplicationClass {
private String name;
@JsonProperty("creation_date")
private Instant creationDate;
@JsonProperty("created_by")
private String createdBy;
// getter, setters, etc.
}
Как @M. Deinum указал в комментариях, требуемую точность можно настроить с помощью application.properties:
spring.jackson.deserialization.READ_DATE_TIMESTAMPS_AS_NANOSECONDS=false
И мы можем удалить оба компонента (сопоставитель и построитель сопоставителя), объявленные в классе конфигурации в приведенном выше примере, потому что JacksonAutoConfiguration захватит и зарегистрирует все известные модули при запуске приложения.
Объявление MyLocalApplicationClass будет идентично показанному выше (creationDate имеет тип java.time.Instant).
Наконец, мы можем реализовать собственный десериализатор, расширив StdDeserializer и переопределив его abstract метод deserialize().
public class DateTimeDeserializer extends StdDeserializer<LocalDateTime> {
public DateTimeDeserializer() {
super(LocalDateTime.class);
}
@Override
public LocalDateTime deserialize(JsonParser p,
DeserializationContext ctxt) throws IOException, JacksonException {
JsonNode node = p.getCodec().readTree(p);
long timestamp = node.longValue();
return Instant
.ofEpochMilli(timestamp)
.atZone(ZoneOffset.UTC)
.toLocalDateTime();
}
}
Чтобы дать Джексону указание применить этот десериализатор, его можно указать с помощью свойства using аннотации @JsonDeserialize, которая должна быть размещена поверх поля creationDate.
public class MyLocalApplicationClass {
private String name;
@JsonDeserialize(using = DateTimeDeserializer.class)
@JsonProperty("creation_date")
private LocalDateTime creationDate;
@JsonProperty("created_by")
private String createdBy;
// getter, setters, etc.
}
@ M.Deinum Спасибо, это ценное предложение. Я знаю, что модули могут быть объявлены как зарегистрированные автоматически, но я не знал, что точность метки времени можно настроить в свойствах приложения. Я улучшил ответ и включил эту информацию.
Spring автоматически обнаружит хорошо известные модули, для этого вам не нужно ничего делать. Вам даже не нужен компонент, полностью откажитесь от конфигурации.
@M.Deinum Спасибо, спасибо, ага момент ... Итак, я неправильно прочитал документацию «авторегистрация для всех bean-компонентов модуля» (третий пункт), только пользовательские модули должны быть объявлены как bean-компоненты, и все стандартные модули, поставляемые Джексоном, будут обнаружены автоматически, верно?
Не только стандартные модули, поставляемые Джексоном, но и другие известные модули также зарегистрированы (например, модуль гибернации).
Jackson2ObjectMapperBuilderавтоматически определяетJavaTimeModule, что вам не нужно добавлять. Вы также можете установитьspring.jackson.deserialization.READ_DATE_TIMESTAMPS_AS_NANOSECONDS=false, а затем полностью отказаться от конфигурации.