From 020c67ec7d5d2a116d94fa5733df21e7cbe24ea9 Mon Sep 17 00:00:00 2001 From: Steven Aerts Date: Wed, 7 May 2025 06:30:52 +0000 Subject: [PATCH] AVRO-4135: [Java] JSON decoding unqualified union types Support unqualified type references for unions when decoding them from json. AVRO-2287 makes it unclear if type reference for a JSON encoded union needs to be qualified or not. Today all encoders use the fully qualified types except the C JSON encoder which uses the unqualified type. In this patch we make the java JSON Decoder more lenient and let it fallback to unqualified types names when no qualified type name matches. Which matches the behavior currently implemented in the Javascript Json decoder. This patch is an alternative for apache/avro#3373 where it is proposed to update the C library instead. --- .../main/java/org/apache/avro/io/parsing/Symbol.java | 10 ++++++++++ .../java/org/apache/avro/io/TestJsonDecoder.java | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java b/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java index a18f3fdbcd5..7009de99735 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java +++ b/lang/java/avro/src/main/java/org/apache/avro/io/parsing/Symbol.java @@ -475,6 +475,16 @@ public int findLabel(String label) { return i; } } + // AVRO-4135 We are a bit more flexible here since we have to deal with the C + // serializer being less strict, so + // we fall back to looking up unqualified names. + for (int i = 0; i < labels.length; i++) { + String candidate = labels[i]; + if (candidate != null && candidate.length() > label.length() + && candidate.charAt(candidate.length() - label.length() - 1) == '.' && candidate.endsWith(label)) { + return i; + } + } } return -1; } diff --git a/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java b/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java index 05057139600..39600af5d4d 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java +++ b/lang/java/avro/src/test/java/org/apache/avro/io/TestJsonDecoder.java @@ -92,4 +92,16 @@ void testIntWithError() throws IOException { JsonDecoder decoder = DecoderFactory.get().jsonDecoder(schema, record); Assertions.assertThrows(AvroTypeException.class, () -> reader.read(null, decoder)); } + + @Test + void testUnionTypeQualification() throws Exception { + final Schema schema = SchemaBuilder.unionOf().nullType().and() + .type(SchemaBuilder.record("r").namespace("space").fields().endRecord()).endUnion(); + GenericDatumReader reader = new GenericDatumReader<>(schema, schema); + JsonDecoder decoder = DecoderFactory.get().jsonDecoder(schema, "{\"space.r\": {}}"); + assertEquals("space.r", reader.read(null, decoder).getSchema().getFullName()); + // AVRO-4135: C json encoder uses unqualified types. + decoder = DecoderFactory.get().jsonDecoder(schema, "{\"r\": {}}"); + assertEquals("space.r", reader.read(null, decoder).getSchema().getFullName()); + } }