Усилить миллисекунды при сериализации мгновенного сообщения в соответствии с ISO8601 с использованием Джексона

У меня есть несколько вопросов, связанных с сериализацией JSON с использованием Jackson в проекте, где я использую Spring Boot 2.0.0.M6, Spring Framework 5.0.1.RELEASE и Jackson 2.9.2.

Я настроил следующие параметры, связанные с Джексоном, в application.properties:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

Сериализация работает в основном так, как мне нужно. Тем не менее, я заметил, что Джексон, кажется, отсекает миллисекунды, если они 000.

Тест 1. Сериализация Instant с миллисекундами, установленными на 000:

  • Инициализировать поле Instant, используя Instant.parse("2017-09-14T04:28:48.000Z")
  • Сериализуйте его с помощью Джексона
  • Выход будет "2017-09-14T04:28:48Z"

Тест 2. Сериализация Instant с установленным в миллисекундах значением, отличным от 000:

  • Инициализировать поле Instant, используя Instant.parse("2017-09-14T04:28:48.100Z")
  • Сериализуйте его с помощью Джексона
  • Выход будет "2017-09-14T04:28:48.100Z"

Вопросы:

  • Такое поведение было задумано?
  • Могу ли я что-нибудь сделать, чтобы вызвать сериализацию 000?

person André Gasser    schedule 26.11.2017    source источник
comment
Похоже на эту проблему github github.com/FasterXML/jackson-datatype-jsr310/ вопросов / 39   -  person Sean Carroll    schedule 27.11.2017
comment
@SeanCarroll Да, действительно. Спасибо, что указали на это.   -  person André Gasser    schedule 27.11.2017


Ответы (5)


Я решаю, используя этот подход:

ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(Instant.class, new InstantSerializerWithMilliSecondPrecision());
objectMapper.registerModule(module);

А для InstantSerializerWithMilliSecondPrecision я использовал это:

public class InstantSerializerWithMilliSecondPrecision extends InstantSerializer {

    public InstantSerializerWithMilliSecondPrecision() {
        super(InstantSerializer.INSTANCE, false, new DateTimeFormatterBuilder().appendInstant(3).toFormatter());
    }
}

Теперь мгновенная сериализация всегда включает миллисекунды. Пример: 2019-09-27T02: 59: 59.000Z

person Gustavo    schedule 24.09.2019
comment
Не публикуйте один и тот же ответ на несколько вопросов stackoverflow.com/questions/45276807/ - person Marco13; 24.09.2019

Похоже, здесь есть проблема с Джексоном, здесь *. Эта ссылка содержит два обходных пути

Временное решение 1

 ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JavaTimeModule());
    SimpleModule module = new SimpleModule();
    module.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
        @Override
        public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ").format(zonedDateTime));
        }
    });
    objectMapper.registerModule(module);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

Временное решение 2

JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(ZonedDateTime.class,
  new ZonedDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")));
ObjectMapper mapper = new ObjectMapper().registerModule(javaTimeModule);

* Ссылка мертва, потому что они устарели от FasterXML / jackson-datatype-jsr310 и переместили ее в jackson -modules-java8. См. https://github.com/FasterXML/jackson-modules-java8/issues/76

person Sean Carroll    schedule 27.11.2017
comment
К сожалению, при сериализации у моих Instants все еще нет .000. Пробовал свой первый обходной путь. Я создал класс конфигурации и добавил bean-компонент ObjectMapper, используя аннотацию Primary, чтобы переопределить автоматически настроенный сопоставитель объектов. - person André Gasser; 27.11.2017
comment
Пробовал Джексон 2.9.5. и эта проблема до сих пор не исправлена. Но ваш 1-й обходной путь работает, спасибо - person vanillaSugar; 18.04.2018
comment
Я искал здесь github.com/FasterXML/ jackson-modules-java8 / за проблему .. может это та самая? github.com/FasterXML/jackson-modules-java8/issues/70 - person vanillaSugar; 18.04.2018
comment
У меня работает обходной путь 1. И вы должны позаботиться о порядке зарегистрированных модулей. - person Max Peng; 09.04.2019
comment
Далее проблема переходит в секунды, если миллисекунды и секунды равны 000 и 00 соответственно. Если я использую 2020-02-04T08: 26.00.000Z, мой вывод будет 2020-02-04T08: 26Z. Какое-либо обходное решение для этого? - person Bhupesh_decoder; 12.11.2020

Ни один из двух обходных путей, упомянутых Шоном Кэрроллом, меня не работает. В итоге я написал свой собственный сериализатор для Instant.

final ObjectMapper mapper = new ObjectMapper();
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Instant.class, new KeepMillisecondInstantSerializer());
mapper.registerModule(javaTimeModule);

public class KeepMillisecondInstantSerializer extends JsonSerializer<Instant> {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
            .withZone(ZoneId.of("UTC"));

    @Override
    public void serialize(final Instant instant, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
        final String serializedInstant = dateTimeFormatter.format(instant);
        jsonGenerator.writeString(serializedInstant);
    }
}

Думаю, Джексон использовать метод Instant.toString() для сериализации мгновенных объектов по умолчанию. Я также нахожу некоторые обсуждение Instant.toString() метода в StackOverflow.

person Sher10ck    schedule 05.09.2018

Вместо того, чтобы исправлять ошибку библиотеки Джексона, можно быстро обойти следующее: Создайте строковую переменную в классе POJO, где у вас есть переменная Timestamp:

private Timestamp createTimeStamp;

private String stringCreateTimeStamp;   

захватить значение временной метки в виде строки:

listOfPojo.forEach(pojo-> {
            pojo.setStringCreateTimeStamp(request.getcreateTimeStamp().toString());
        });

Обратитесь к https://www.baeldung.com/java-string-to-timestamp для конверсий

person Jaya    schedule 27.05.2020

Решите эту проблему, используя настраиваемые сериализаторы для классов LocalDateTime и ZonedDateTime.

Мое решение работает для меня, потому что я использую только эти два класса в ответах API для представления даты и времени! Я не использую Instant или Date, поэтому обратите на них внимание.

@Configuration
class JacksonConfig {

@Bean
fun objectMapper(): ObjectMapper {
    val mapper = ObjectMapper()
    val javaTimeModule = JavaTimeModule().apply {
        addSerializer(LocalDateTime::class.java, KeepMillisecondLocalDateTimeSerializer())
        addSerializer(ZonedDateTime::class.java, KeepMillisecondZonedDateTimeSerializer())
    }
    mapper.registerModule(javaTimeModule)
    return mapper
}

class KeepMillisecondZonedDateTimeSerializer : JsonSerializer<ZonedDateTime>() {
    private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")

    @Throws(IOException::class)
    override fun serialize(
        value: ZonedDateTime,
        jsonGenerator: JsonGenerator,
        serializerProvider: SerializerProvider?
    ) {
        jsonGenerator.writeString(formatter.format(value))
    }
}

class KeepMillisecondLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
    private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")

    @Throws(IOException::class)
    override fun serialize(
        value: LocalDateTime,
        jsonGenerator: JsonGenerator,
        serializerProvider: SerializerProvider?
    ) {
        jsonGenerator.writeString(formatter.format(value))
    }
}
}
person Ivan Rudnev    schedule 11.09.2020