From 2865f882097ddeb6e1682c6b9f25d5a069dc212b Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 19 Apr 2026 16:06:45 -0700 Subject: [PATCH 1/2] Fix #2844: duplicated property with external type id --- .../databind/ser/bean/BeanSerializerBase.java | 103 ++++++++++++++++++ .../ext/ExternalTypeIdDuplicate2844Test.java | 85 +++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 src/test/java/tools/jackson/databind/jsontype/ext/ExternalTypeIdDuplicate2844Test.java diff --git a/src/main/java/tools/jackson/databind/ser/bean/BeanSerializerBase.java b/src/main/java/tools/jackson/databind/ser/bean/BeanSerializerBase.java index 7285d91e33..bdf7b805aa 100644 --- a/src/main/java/tools/jackson/databind/ser/bean/BeanSerializerBase.java +++ b/src/main/java/tools/jackson/databind/ser/bean/BeanSerializerBase.java @@ -80,6 +80,15 @@ public abstract class BeanSerializerBase */ final protected JsonFormat.Shape _serializationShape; + /** + * Name of a bean property whose serialized name equals the + * {@code EXTERNAL_PROPERTY} type id name of this type's polymorphic + * {@link TypeSerializer}, if any. Computed in {@link #resolve}. When + * non-null, {@link #serializeWithType} suppresses the otherwise-emitted + * duplicate type id key so the bean's own property wins ([databind#2844]). + */ + protected String _externalTypeIdToSuppress; + /* /********************************************************************** /* Life-cycle: constructors @@ -137,6 +146,7 @@ protected BeanSerializerBase(BeanSerializerBase src, _objectIdWriter = src._objectIdWriter; _propertyFilterId = src._propertyFilterId; _serializationShape = src._serializationShape; + _externalTypeIdToSuppress = src._externalTypeIdToSuppress; } protected BeanSerializerBase(BeanSerializerBase src, @@ -157,6 +167,7 @@ protected BeanSerializerBase(BeanSerializerBase src, _objectIdWriter = objectIdWriter; _propertyFilterId = filterId; _serializationShape = src._serializationShape; + _externalTypeIdToSuppress = src._externalTypeIdToSuppress; } protected BeanSerializerBase(BeanSerializerBase src, Set toIgnore, Set toInclude) @@ -189,6 +200,22 @@ protected BeanSerializerBase(BeanSerializerBase src, Set toIgnore, Set Date: Sun, 19 Apr 2026 16:09:06 -0700 Subject: [PATCH 2/2] simplifi --- .../jsontype/ext/ExternalTypeIdDuplicate2844Test.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/tools/jackson/databind/jsontype/ext/ExternalTypeIdDuplicate2844Test.java b/src/test/java/tools/jackson/databind/jsontype/ext/ExternalTypeIdDuplicate2844Test.java index eda950c1b7..bd5e43e3fc 100644 --- a/src/test/java/tools/jackson/databind/jsontype/ext/ExternalTypeIdDuplicate2844Test.java +++ b/src/test/java/tools/jackson/databind/jsontype/ext/ExternalTypeIdDuplicate2844Test.java @@ -52,15 +52,13 @@ public void testDirectSerializationSuppressesDuplicateTypeId() throws Exception } // Targeting the subtype directly should also not emit two type keys. + // The reported bug output was: {"type":"dog","type":"GermanShepherd"} @Test public void testDirectSerializationTargetingSubtype() throws Exception { Dog dog = new Dog("GermanShepherd"); String json = MAPPER.writeValueAsString(dog); - int firstIdx = json.indexOf("\"type\""); - int secondIdx = json.indexOf("\"type\"", firstIdx + 1); - assertEquals(-1, secondIdx, - "Should not emit duplicate 'type' keys; got: " + json); + assertEquals(a2q("{'type':'GermanShepherd'}"), json); } // Regression: wrapped case must still round-trip correctly. The outer