From d0faed018ded11430a82b7f8f1202eaf60e1b6c4 Mon Sep 17 00:00:00 2001 From: Benoit Lacelle Date: Mon, 27 Oct 2025 02:22:43 +0400 Subject: [PATCH] Always considering _forceTypeInformation, _forceTypeInformation is defaulted to false, add testCases for #5035 --- .../databind/ser/std/JsonValueSerializer.java | 29 +++- ...ntConstructor_Enum_WeirdSerialization.java | 61 +++++++++ ...TestIntConstructor_WeirdSerialization.java | 124 ++++++++++++++++++ 3 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_Enum_WeirdSerialization.java create mode 100644 src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_WeirdSerialization.java diff --git a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java index a60bf82268..214f913ff3 100644 --- a/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java +++ b/src/main/java/com/fasterxml/jackson/databind/ser/std/JsonValueSerializer.java @@ -119,6 +119,22 @@ public JsonValueSerializer(AnnotatedMember accessor, _dynamicSerializers = PropertySerializerMap.emptyForProperties(); } + @SuppressWarnings("unchecked") + public JsonValueSerializer(AnnotatedMember accessor, + TypeSerializer vts, JsonSerializer ser, + Set ignoredProperties, boolean forceTypeInformation) + { + super(accessor.getType()); + _accessor = accessor; + _valueType = accessor.getType(); + _valueTypeSerializer = vts; + _valueSerializer = (JsonSerializer) ser; + _property = null; + _forceTypeInformation = forceTypeInformation; // gets reconsidered when we are contextualized + _ignoredProperties = ignoredProperties; + _dynamicSerializers = PropertySerializerMap.emptyForProperties(); + } + @Deprecated // since 2.16 public JsonValueSerializer(AnnotatedMember accessor, TypeSerializer vts, JsonSerializer ser) @@ -161,7 +177,8 @@ public static JsonValueSerializer construct(SerializationConfig config, .findPropertyIgnoralByName(config, accessor); final Set ignoredProperties = ignorals.findIgnoredForSerialization(); ser = _withIgnoreProperties(ser, ignoredProperties); - return new JsonValueSerializer(accessor, vts, ser, ignoredProperties); + // TODO Try `_forceTypeInformation` with false as default + return new JsonValueSerializer(accessor, vts, ser, ignoredProperties, false); } @SuppressWarnings("unchecked") @@ -311,7 +328,10 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider JsonSerializer ser = _valueSerializer; if (ser == null) { // no serializer yet? Need to fetch ser = _findDynamicSerializer(ctxt, value.getClass()); - } else { + } + + // TODO `_forceTypeInformation` should be checked even if ser is not null +// else { // 09-Dec-2010, tatu: To work around natural type's refusal to add type info, we do // this (note: type is for the wrapper type, not enclosed value!) if (_forceTypeInformation) { @@ -322,13 +342,14 @@ public void serializeWithType(Object bean, JsonGenerator gen, SerializerProvider typeSer0.writeTypeSuffix(gen, typeIdDef); return; - } - } + } else { +// } // 28-Sep-2016, tatu: As per [databind#1385], we do need to do some juggling // to use different Object for type id (logical type) and actual serialization // (delegate type). TypeSerializerRerouter rr = new TypeSerializerRerouter(typeSer0, bean); ser.serializeWithType(value, gen, ctxt, rr); + } } /* diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_Enum_WeirdSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_Enum_WeirdSerialization.java new file mode 100644 index 0000000000..b5efda6e94 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_Enum_WeirdSerialization.java @@ -0,0 +1,61 @@ +package com.fasterxml.jackson.databind.ser.benoit; + +import java.util.Locale; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class TestIntConstructor_Enum_WeirdSerialization { + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type", + defaultImpl = NativeOption.class) + public interface SomeOption { + } + + public static enum NativeOption implements SomeOption { + A, B; + + @JsonCreator + public static NativeOption forValue(String value) { + return NativeOption.valueOf(value.toUpperCase(Locale.US)); + } + + } + + public static enum CustomOption implements SomeOption { + C, D; + + @JsonCreator + public static NativeOption forValue(String value) { + return NativeOption.valueOf(value.toUpperCase(Locale.US)); + } + + } + + @Test + public void testNative_string() throws JsonProcessingException { + NativeOption matcher = NativeOption.A; + + ObjectMapper objectMapper = new ObjectMapper(); + + String asString = objectMapper.writeValueAsString(matcher); + Assertions.assertThat(asString).isEqualTo("A"); + } + + @Test + public void testCustom_string() throws JsonProcessingException { + CustomOption matcher = CustomOption.C; + + ObjectMapper objectMapper = new ObjectMapper(); + + String asString = objectMapper.writeValueAsString(matcher); + Assertions.assertThat(asString).isEqualTo("type: CustomOption, value: A"); + } +} \ No newline at end of file diff --git a/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_WeirdSerialization.java b/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_WeirdSerialization.java new file mode 100644 index 0000000000..475b9b6ee0 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/databind/ser/benoit/TestIntConstructor_WeirdSerialization.java @@ -0,0 +1,124 @@ +package com.fasterxml.jackson.databind.ser.benoit; + +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +// https://github.com/FasterXML/jackson-databind/issues/5035 +public class TestIntConstructor_WeirdSerialization { + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.PROPERTY, + property = "type", + defaultImpl = AroundString.class) + @JsonSubTypes({ @JsonSubTypes.Type(value = AroundString.class, name = "string"), + @JsonSubTypes.Type(value = AroundObject.class, name = "object") }) + public interface AroundSomething { + Object getInner(); + } + + public static class AroundString implements AroundSomething { + @JsonValue + String inner; // <-- Turning this to Object will make the test passes + + @Override + public Object getInner() { + return inner; + } + + public void setInner(String inner) { + this.inner = inner; + } + + } + + public static class AroundObject implements AroundSomething { + @JsonValue + Object inner; // <-- Turning this to Object will make the test passes + + @Override + public Object getInner() { + return inner; + } + + public void setInner(String inner) { + this.inner = inner; + } + + } + + public static class HasFromObject { + AroundSomething c; + + public AroundSomething getC() { + return c; + } + + public void setC(AroundSomething c) { + this.c = c; + } + } + + @Test + public void test_aroundString_convertValue() throws JsonProcessingException { + AroundString matcher = new AroundString(); + matcher.setInner("foo"); + + HasFromObject wrapper = new HasFromObject(); + wrapper.setC(matcher); + + ObjectMapper objectMapper = new ObjectMapper(); + + Map asMap = objectMapper.convertValue(wrapper, Map.class); + Assertions.assertThat(asMap.toString()).isEqualTo("{c=foo}"); + } + + @Test + public void test_aroundString_writeValueAsString() throws JsonProcessingException { + AroundString matcher = new AroundString(); + matcher.setInner("foo"); + + HasFromObject wrapper = new HasFromObject(); + wrapper.setC(matcher); + + ObjectMapper objectMapper = new ObjectMapper(); + + String asString = objectMapper.writeValueAsString(wrapper); + Assertions.assertThat(asString).isEqualTo("{\"c\":\"foo\"}"); // FAILs with `"{"c":["from","foo"]}"` + } + + @Test + public void test_aroundObject_convertValue() throws JsonProcessingException { + AroundObject matcher = new AroundObject(); + matcher.setInner("foo"); + + HasFromObject wrapper = new HasFromObject(); + wrapper.setC(matcher); + + ObjectMapper objectMapper = new ObjectMapper(); + + Map asMap = objectMapper.convertValue(wrapper, Map.class); + Assertions.assertThat(asMap.toString()).isEqualTo("{c=foo}"); + } + + @Test + public void test_aroundObject_writeValueAsString() throws JsonProcessingException { + AroundObject matcher = new AroundObject(); + matcher.setInner("foo"); + + HasFromObject wrapper = new HasFromObject(); + wrapper.setC(matcher); + + ObjectMapper objectMapper = new ObjectMapper(); + + String asString = objectMapper.writeValueAsString(wrapper); + Assertions.assertThat(asString).isEqualTo("{\"c\":\"foo\"}"); // FAILs with `"{"c":["from","foo"]}"` + } +} \ No newline at end of file