From 79a30b973bfbe6178769383dd4a3ffad74406867 Mon Sep 17 00:00:00 2001 From: Aditya Date: Thu, 24 Sep 2026 21:22:19 +0530 Subject: [PATCH] Fix #812: fall back when an Avro type id is not a class. A missing record name should read as the base type again, so Object becomes a Map. Illegal subtype errors still throw. Co-authored-by: Cursor --- .../dataformat/avro/AvroTypeDeserializer.java | 7 +++---- .../dataformat/avro/AvroTypeIdResolver.java | 19 +++++++++---------- .../records/RecordWithMissingTypeTest.java} | 15 ++++----------- 3 files changed, 16 insertions(+), 25 deletions(-) rename avro/src/test/java/tools/jackson/dataformat/avro/{tofix/RecordWithMissingType812Test.java => interop/records/RecordWithMissingTypeTest.java} (80%) diff --git a/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeDeserializer.java b/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeDeserializer.java index 8942ba07f..7775fe086 100644 --- a/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeDeserializer.java +++ b/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeDeserializer.java @@ -65,12 +65,11 @@ public Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) protected JavaType _handleUnknownTypeId(DeserializationContext ctxt, String typeId) throws JacksonException { - // 29-Jan-2018, tatu: No idea why this was added in 2.x. - /* - if (ctxt.hasValueDeserializerFor(_baseType, null)) { + // [dataformats-binary#812]: 3.x has no hasValueDeserializerFor(). + // If the base type can be read (Object becomes a Map), use it. + if (ctxt.findNonContextualValueDeserializer(_baseType) != null) { return _baseType; } - */ return super._handleUnknownTypeId(ctxt, typeId); } } diff --git a/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeIdResolver.java b/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeIdResolver.java index 96d1dd224..260e2cd4c 100644 --- a/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeIdResolver.java +++ b/avro/src/main/java/tools/jackson/dataformat/avro/AvroTypeIdResolver.java @@ -6,6 +6,7 @@ import tools.jackson.databind.DatabindContext; import tools.jackson.databind.JavaType; +import tools.jackson.databind.exc.InvalidTypeIdException; import tools.jackson.databind.jsontype.NamedType; import tools.jackson.databind.jsontype.PolymorphicTypeValidator; import tools.jackson.databind.jsontype.impl.ClassNameIdResolver; @@ -44,18 +45,16 @@ protected JavaType _typeFromId(DatabindContext ctxt, String id) if (subType != null) { id = _idFrom(ctxt, null, subType); } - return super._typeFromId(ctxt, id); - - // 26-Nov-2019, tatu: Should not swallow exceptions; with 2.10+ we can get - // "Illegal subtype" accidentally and that should be propagated -/* try { return super._typeFromId(ctxt, id); - } catch (InvalidTypeIdException | IllegalArgumentException e) { - // AvroTypeDeserializer expects null if we can't map the type ID to a class; It will throw an appropriate error if we can't - // find a usable type. - return null; + } catch (InvalidTypeIdException e) { + // [dataformats-binary#812]: only a missing class should fall back. + // Illegal subtype and PolymorphicTypeValidator failures still throw. + String msg = e.getMessage(); + if (msg != null && msg.contains("no such class found")) { + return null; + } + throw e; } -*/ } } diff --git a/avro/src/test/java/tools/jackson/dataformat/avro/tofix/RecordWithMissingType812Test.java b/avro/src/test/java/tools/jackson/dataformat/avro/interop/records/RecordWithMissingTypeTest.java similarity index 80% rename from avro/src/test/java/tools/jackson/dataformat/avro/tofix/RecordWithMissingType812Test.java rename to avro/src/test/java/tools/jackson/dataformat/avro/interop/records/RecordWithMissingTypeTest.java index 1a7260ed6..cfa6c5f14 100644 --- a/avro/src/test/java/tools/jackson/dataformat/avro/tofix/RecordWithMissingType812Test.java +++ b/avro/src/test/java/tools/jackson/dataformat/avro/interop/records/RecordWithMissingTypeTest.java @@ -1,4 +1,4 @@ -package tools.jackson.dataformat.avro.tofix; +package tools.jackson.dataformat.avro.interop.records; import java.io.IOException; import java.util.Map; @@ -8,21 +8,15 @@ import com.fasterxml.jackson.annotation.JsonProperty; -import tools.jackson.dataformat.avro.testutil.failure.JacksonTestFailureExpected; - import static org.assertj.core.api.Assertions.assertThat; import static tools.jackson.dataformat.avro.interop.ApacheAvroInteropUtil.getJacksonSchema; import static tools.jackson.dataformat.avro.interop.ApacheAvroInteropUtil.jacksonDeserialize; import static tools.jackson.dataformat.avro.interop.ApacheAvroInteropUtil.jacksonSerialize; import static tools.jackson.dataformat.avro.interop.InteropTestBase.type; -// [dataformats-binary#812]: passes on 2.x, where an unresolvable type id falls -// back to the base type (here `Object`, so result is a `Map`): on 3.x that fallback -// was removed from `AvroTypeIdResolver` and `AvroTypeDeserializer._handleUnknownTypeId()`, -// so `InvalidTypeIdException` is thrown instead. -// (test never ran before as class name lacked `Test` suffix; namespace also changed -// from "bad-namespace" as Avro 1.12 rejects "-" in namespaces) -public class RecordWithMissingType812Test { +// [dataformats-binary#812]: an Avro record name that is not a Java class falls +// back to the base type. For Object that is a Map. +public class RecordWithMissingTypeTest { public static class WrapperOuter { @@ -56,7 +50,6 @@ public static class Holder { // [dataformats-binary#812] @SuppressWarnings("unchecked") - @JacksonTestFailureExpected @Test public void testRecordWithPolymorphicKeyDeserialization() throws IOException { Schema schema = getJacksonSchema(type(WrapperOuter.class, type(WrapperInner.class, type(Holder.class, Double.class))));