From b3d7c84b061a2be688e752d3b4be10efed7649e0 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 20 Nov 2023 20:17:59 -0800 Subject: [PATCH] Fix #288: consider custom serializer additions via `JavaTimeModule` --- .../datatype/jsr310/JavaTimeModule.java | 22 +++++++++ .../jsr310/ser/LocalDateTimeSerTest.java | 45 ++++++++++--------- release-notes/VERSION-2.x | 6 +++ 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java index f95eb87a..fe42ebe9 100644 --- a/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java +++ b/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java @@ -140,7 +140,13 @@ public void setupModule(SetupContext context) { desers.addDeserializer(YearMonth.class, YearMonthDeserializer.INSTANCE); desers.addDeserializer(ZoneId.class, JSR310StringParsableDeserializer.ZONE_ID); desers.addDeserializer(ZoneOffset.class, JSR310StringParsableDeserializer.ZONE_OFFSET); + context.addDeserializers(desers); + // 20-Nov-2023, tatu: [modules-java8#288]: someone may have directly + // added entries, need to add for backwards compatibility + if (_deserializers != null) { + context.addDeserializers(desers); + } SimpleSerializers sers = new SimpleSerializers(); @@ -169,11 +175,21 @@ public void setupModule(SetupContext context) { sers.addSerializer(ZoneOffset.class, new ToStringSerializer(ZoneOffset.class)); context.addSerializers(sers); + // 20-Nov-2023, tatu: [modules-java8#288]: someone may have directly + // added entries, need to add for backwards compatibility + if (_serializers != null) { + context.addSerializers(_serializers); + } // key serializers SimpleSerializers keySers = new SimpleSerializers(); keySers.addSerializer(ZonedDateTime.class, ZonedDateTimeKeySerializer.INSTANCE); context.addKeySerializers(keySers); + // 20-Nov-2023, tatu: [modules-java8#288]: someone may have directly + // added entries, need to add for backwards compatibility + if (_keySerializers != null) { + context.addKeySerializers(_keySerializers); + } // key deserializers SimpleKeyDeserializers keyDesers = new SimpleKeyDeserializers(); @@ -191,7 +207,13 @@ public void setupModule(SetupContext context) { keyDesers.addDeserializer(ZonedDateTime.class, ZonedDateTimeKeyDeserializer.INSTANCE); keyDesers.addDeserializer(ZoneId.class, ZoneIdKeyDeserializer.INSTANCE); keyDesers.addDeserializer(ZoneOffset.class, ZoneOffsetKeyDeserializer.INSTANCE); + context.addKeyDeserializers(keyDesers); + // 20-Nov-2023, tatu: [modules-java8#288]: someone may have directly + // added entries, need to add for backwards compatibility + if (_keyDeserializers != null) { + context.addKeyDeserializers(_keyDeserializers); + } context.addValueInstantiators(new ValueInstantiators.Base() { @Override diff --git a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/LocalDateTimeSerTest.java b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/LocalDateTimeSerTest.java index d43a2fd2..a34a71ec 100644 --- a/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/LocalDateTimeSerTest.java +++ b/datetime/src/test/java/com/fasterxml/jackson/datatype/jsr310/ser/LocalDateTimeSerTest.java @@ -18,11 +18,14 @@ import java.time.LocalDateTime; import java.time.Month; +import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.MockObjectConfiguration; import com.fasterxml.jackson.datatype.jsr310.ModuleTestBase; @@ -40,25 +43,22 @@ static class LDTWrapper { public LDTWrapper(LocalDateTime v) { value = v; } } - private final ObjectMapper mapper = newMapper(); + private final static ObjectMapper MAPPER = newMapper(); @Test public void testSerializationAsTimestamp01() throws Exception { LocalDateTime time = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43); - String value = mapper.writeValueAsString(time); - - assertNotNull("The value should not be null.", value); - assertEquals("The value is not correct.", "[1986,1,17,15,43]", value); + assertEquals("The value is not correct.", "[1986,1,17,15,43]", + MAPPER.writeValueAsString(time)); } @Test public void testSerializationAsTimestamp02() throws Exception { LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 57); - String value = mapper.writeValueAsString(time); + String value = MAPPER.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[2013,8,21,9,22,57]", value); } @@ -66,12 +66,10 @@ public void testSerializationAsTimestamp02() throws Exception public void testSerializationAsTimestamp03Nanosecond() throws Exception { LocalDateTime time = LocalDateTime.of(2013, Month.AUGUST, 21, 9, 22, 0, 57); - - ObjectMapper m = newMapper().enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) - .enable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); - String value = m.writeValueAsString(time); - - assertNotNull("The value should not be null.", value); + String value = MAPPER.writer() + .with(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .with(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) + .writeValueAsString(time); assertEquals("The value is not correct.", "[2013,8,21,9,22,0,57]", value); } @@ -83,7 +81,6 @@ public void testSerializationAsTimestamp03Millisecond() throws Exception ObjectMapper m = newMapper().disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[2013,8,21,9,22,0,0]", value); } @@ -96,7 +93,6 @@ public void testSerializationAsTimestamp04Nanosecond() throws Exception .enable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[2005,11,5,22,31,5,829837]", value); } @@ -109,7 +105,6 @@ public void testSerializationAsTimestamp04Millisecond() throws Exception .disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[2005,11,5,22,31,5,422]", value); } @@ -157,7 +152,7 @@ public void testSerializationWithFormatOverride() throws Exception { LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 999000); assertEquals(a2q("{'value':'2005-11-05A22:31:05'}"), - mapper.writeValueAsString(new LDTWrapper(time))); + MAPPER.writeValueAsString(new LDTWrapper(time))); ObjectMapper m = mapperBuilder().withConfigOverride(LocalDateTime.class, cfg -> cfg.setFormat(JsonFormat.Value.forPattern("yyyy-MM-dd'X'HH:mm"))) @@ -176,7 +171,6 @@ public void testSerializationWithTypeInfo01() throws Exception m.addMixIn(Temporal.class, MockObjectConfiguration.class); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,829837]]", value); } @@ -192,7 +186,6 @@ public void testSerializationWithTypeInfo02() throws Exception LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 422829837); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[\"" + LocalDateTime.class.getName() + "\",[2005,11,5,22,31,5,422]]", value); } @@ -206,8 +199,20 @@ public void testSerializationWithTypeInfo03() throws Exception LocalDateTime time = LocalDateTime.of(2005, Month.NOVEMBER, 5, 22, 31, 5, 829837); String value = m.writeValueAsString(time); - assertNotNull("The value should not be null.", value); assertEquals("The value is not correct.", "[\"" + LocalDateTime.class.getName() + "\",\"" + time.toString() + "\"]", value); } + + // [modules-java8#288] + @Test + public void testExplicitConstructionWithPattern() throws Exception + { + ObjectMapper mapper = JsonMapper.builder() + .addModule(new JavaTimeModule() + .addSerializer(LocalDateTime.class, + new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("MM/dd/yyyy"))) + ).build(); + LocalDateTime time = LocalDateTime.of(1986, Month.JANUARY, 17, 15, 43); + assertEquals(q("01/17/1986"), mapper.writeValueAsString(time)); + } } diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index ba53d659..7b22977f 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -8,6 +8,12 @@ Modules: === Releases === ------------------------------------------------------------------------ +2.16.1 (not yet released) + +#288: `LocalDateTime` serialization issue with custom-configured + `LocalDateTimeSerializer` + (reported by @mklinkj) + 2.16.0 (15-Nov-2023) #263: Add `JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_TIMESTAMPS` to allow parsing