diff --git a/lang/java/avro/src/main/java/org/apache/avro/reflect/AvroNamespace.java b/lang/java/avro/src/main/java/org/apache/avro/reflect/AvroNamespace.java new file mode 100644 index 00000000000..ea89b44e4aa --- /dev/null +++ b/lang/java/avro/src/main/java/org/apache/avro/reflect/AvroNamespace.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.avro.reflect; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Sets the avrotypename for this java type. When reading into this class, a + * reflectdatumreader looks for a schema field with the avrotypename. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE }) +public @interface AvroNamespace { + String value() default ""; +} diff --git a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java index 347490679ee..5a5ab8ce275 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java +++ b/lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java @@ -531,6 +531,11 @@ public Class getClass(Schema schema) { return Short.TYPE; if (Character.class.getName().equals(intClass)) return Character.TYPE; + case RECORD: + case ENUM: + Class className = getClassProp(schema, CLASS_PROP); + if (className != null) + return className; default: return super.getClass(schema); } @@ -706,9 +711,7 @@ protected Schema createSchema(Type type, Map names) { AvroDoc annotatedDoc = c.getAnnotation(AvroDoc.class); // Docstring String doc = (annotatedDoc != null) ? annotatedDoc.value() : null; String name = c.getSimpleName(); - String space = c.getPackage() == null ? "" : c.getPackage().getName(); - if (c.getEnclosingClass() != null) // nested class - space = c.getEnclosingClass().getName().replace('$', '.'); + String space = getNamespace(c); Union union = c.getAnnotation(Union.class); if (union != null) { // union annotated return getAnnotatedUnion(union, names); @@ -722,10 +725,12 @@ protected Schema createSchema(Type type, Map names) { for (Enum constant : constants) symbols.add(constant.name()); schema = Schema.createEnum(name, doc, space, symbols); + schema.addProp(CLASS_PROP, c.getName()); consumeAvroAliasAnnotation(c, schema); } else if (GenericFixed.class.isAssignableFrom(c)) { // fixed int size = c.getAnnotation(FixedSize.class).value(); schema = Schema.createFixed(name, doc, space, size); + schema.addProp(CLASS_PROP, c.getName()); consumeAvroAliasAnnotation(c, schema); } else if (IndexedRecord.class.isAssignableFrom(c)) { // specific return super.createSchema(type, names); @@ -733,6 +738,7 @@ protected Schema createSchema(Type type, Map names) { List fields = new ArrayList<>(); boolean error = Throwable.class.isAssignableFrom(c); schema = Schema.createRecord(name, doc, space, error); + schema.addProp(CLASS_PROP, c.getName()); consumeAvroAliasAnnotation(c, schema); names.put(c.getName(), schema); for (Field field : getCachedFields(c)) @@ -802,6 +808,27 @@ private String simpleName(Class c) { return simpleName; } + /* + * Function checks if there is @AvroTypeName annotation on the class. If present + * then returns the value of the annotation else returns the package of the + * class + */ + private String getNamespace(Class c) { + AvroNamespace avroNamespace = c.getAnnotation(AvroNamespace.class); + if (avroNamespace != null) { + return avroNamespace.value(); + } + if (c.getEnclosingClass() != null) { // nested class + AvroNamespace enclosingClassAvroNamespace = c.getEnclosingClass().getAnnotation(AvroNamespace.class); + if (enclosingClassAvroNamespace != null) { + return enclosingClassAvroNamespace.value(); + } + return c.getEnclosingClass().getName().replace('$', '.'); + } + + return c.getPackage() == null ? "" : c.getPackage().getName(); + } + private static final Schema THROWABLE_MESSAGE = makeNullable(Schema.create(Schema.Type.STRING)); // if array element type is a class with a union annotation, note it diff --git a/lang/java/avro/src/main/java/org/apache/avro/reflect/package.html b/lang/java/avro/src/main/java/org/apache/avro/reflect/package.html index 3396a8c6d30..bd81f075ab7 100644 --- a/lang/java/avro/src/main/java/org/apache/avro/reflect/package.html +++ b/lang/java/avro/src/main/java/org/apache/avro/reflect/package.html @@ -83,7 +83,10 @@

The {@link org.apache.avro.reflect.AvroName AvroName} annotation renames the field in the schema to the given name. The reflect datum reader will look for a schema field with the given name, when trying to read into such an -annotated java field. +annotated java field. + +

The {@link org.apache.avro.reflect.AvroNamespace AvroTypeName} annotation renames + the namespace in the schema to the given namespace.

The {@link org.apache.avro.reflect.AvroMeta AvroMeta} annotation adds an arbitrary key:value pair in the schema at the node of the java field. diff --git a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java index 50121b5a0dd..52e9bf1891f 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java +++ b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflect.java @@ -565,7 +565,8 @@ public void testAvroNullableDefault() { check(NullableDefaultTest.class, "{\"type\":\"record\",\"name\":\"NullableDefaultTest\"," + "\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"foo\",\"type\":[\"null\",\"int\"],\"default\":1}]}"); + + "{\"name\":\"foo\",\"type\":[\"null\",\"int\"],\"default\":1}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$NullableDefaultTest\"}"); } private static class UnionDefaultTest { @@ -579,7 +580,8 @@ public void testAvroUnionDefault() { check(UnionDefaultTest.class, "{\"type\":\"record\",\"name\":\"UnionDefaultTest\"," + "\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"foo\",\"type\":[\"int\",\"string\"],\"default\":1}]}"); + + "{\"name\":\"foo\",\"type\":[\"int\",\"string\"],\"default\":1}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$UnionDefaultTest\"}"); } @Test @@ -639,8 +641,10 @@ public static enum E { @Test void testEnum() throws Exception { - check(E.class, "{\"type\":\"enum\",\"name\":\"E\",\"namespace\":" - + "\"org.apache.avro.reflect.TestReflect\",\"symbols\":[\"A\",\"B\"]}"); + check(E.class, + "{\"type\":\"enum\",\"name\":\"E\",\"namespace\":" + + "\"org.apache.avro.reflect.TestReflect\",\"symbols\":[\"A\",\"B\"]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$E\"}"); } public static class R { @@ -652,7 +656,8 @@ public static class R { void record() throws Exception { check(R.class, "{\"type\":\"record\",\"name\":\"R\",\"namespace\":" + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"a\",\"type\":\"int\"}," + "{\"name\":\"b\",\"type\":\"long\"}]}"); + + "{\"name\":\"a\",\"type\":\"int\"}," + "{\"name\":\"b\",\"type\":\"long\"}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$R\"}"); } public static class RAvroIgnore { @@ -663,7 +668,7 @@ public static class RAvroIgnore { @Test void annotationAvroIgnore() throws Exception { check(RAvroIgnore.class, "{\"type\":\"record\",\"name\":\"RAvroIgnore\",\"namespace\":" - + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[]}"); + + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"java-class\":\"org.apache.avro.reflect.TestReflect$RAvroIgnore\"}"); } @AvroMeta(key = "X", value = "Y") @@ -677,7 +682,7 @@ void annotationAvroMeta() throws Exception { check(RAvroMeta.class, "{\"type\":\"record\",\"name\":\"RAvroMeta\",\"namespace\":" + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" + "{\"name\":\"a\",\"type\":\"int\",\"K\":\"V\"}]" - + ",\"X\":\"Y\"}"); + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$RAvroMeta\"" + ",\"X\":\"Y\"}"); } @AvroMeta(key = "X", value = "Y") @@ -693,7 +698,8 @@ void annotationMultiAvroMeta() { check(RAvroMultiMeta.class, "{\"type\":\"record\",\"name\":\"RAvroMultiMeta\",\"namespace\":" + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"a\",\"type\":\"int\",\"K\":\"V\",\"L\":\"W\"}]" + ",\"X\":\"Y\",\"A\":\"B\"}"); + + "{\"name\":\"a\",\"type\":\"int\",\"K\":\"V\",\"L\":\"W\"}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$RAvroMultiMeta\"" + ",\"X\":\"Y\",\"A\":\"B\"}"); } public static class RAvroDuplicateFieldMeta { @@ -729,8 +735,10 @@ public static class RAvroName { @Test void annotationAvroName() throws Exception { - check(RAvroName.class, "{\"type\":\"record\",\"name\":\"RAvroName\",\"namespace\":" - + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" + "{\"name\":\"b\",\"type\":\"int\"}]}"); + check(RAvroName.class, + "{\"type\":\"record\",\"name\":\"RAvroName\",\"namespace\":" + + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" + "{\"name\":\"b\",\"type\":\"int\"}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$RAvroName\"}"); } public static class RAvroNameCollide { @@ -756,8 +764,10 @@ public static class RAvroStringableField { @Test void annotationAvroStringableFields() throws Exception { - check(RAvroStringableField.class, "{\"type\":\"record\",\"name\":\"RAvroStringableField\",\"namespace\":" - + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" + "{\"name\":\"a\",\"type\":\"string\"}]}"); + check(RAvroStringableField.class, + "{\"type\":\"record\",\"name\":\"RAvroStringableField\",\"namespace\":" + + "\"org.apache.avro.reflect.TestReflect\",\"fields\":[" + "{\"name\":\"a\",\"type\":\"string\"}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$RAvroStringableField\"}"); } private void check(Object o, String schemaJson) { @@ -883,7 +893,8 @@ void avroEncodeInducing() throws IOException { assertEquals(schm.toString(), "{\"type\":\"record\",\"name\":\"AvroEncRecord\",\"namespace" + "\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[{\"name\":\"date\"," - + "\"type\":{\"type\":\"long\",\"CustomEncoding\":\"DateAsLongEncoding\"}}]}"); + + "\"type\":{\"type\":\"long\",\"CustomEncoding\":\"DateAsLongEncoding\"}}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$AvroEncRecord\"}"); } @Test @@ -1248,11 +1259,11 @@ private static class AliasC { @Test void avroAliasOnClass() { check(AliasA.class, - "{\"type\":\"record\",\"name\":\"AliasA\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"aliases\":[\"b.a\"]}"); + "{\"type\":\"record\",\"name\":\"AliasA\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"java-class\":\"org.apache.avro.reflect.TestReflect$AliasA\",\"aliases\":[\"b.a\"]}"); check(AliasB.class, - "{\"type\":\"record\",\"name\":\"AliasB\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"aliases\":[\"a\"]}"); + "{\"type\":\"record\",\"name\":\"AliasB\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"java-class\":\"org.apache.avro.reflect.TestReflect$AliasB\",\"aliases\":[\"a\"]}"); check(AliasC.class, - "{\"type\":\"record\",\"name\":\"AliasC\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"aliases\":[\"a\"]}"); + "{\"type\":\"record\",\"name\":\"AliasC\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"java-class\":\"org.apache.avro.reflect.TestReflect$AliasC\",\"aliases\":[\"a\"]}"); } @AvroAlias(alias = "alias1", space = "space1") @@ -1264,7 +1275,9 @@ private static class MultipleAliasRecord { @Test void multipleAliasAnnotationsOnClass() { check(MultipleAliasRecord.class, - "{\"type\":\"record\",\"name\":\"MultipleAliasRecord\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"aliases\":[\"space1.alias1\",\"space2.alias2\"]}"); + "{\"type\":\"record\",\"name\":\"MultipleAliasRecord\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$MultipleAliasRecord\"" + + ",\"aliases\":[\"space1.alias1\",\"space2.alias2\"]}"); } @@ -1277,7 +1290,7 @@ void dollarTerminatedNamespaceCompatibility() { Schema s = new Schema.Parser(NameValidator.NO_VALIDATION).parse( "{\"type\":\"record\",\"name\":\"Z\",\"namespace\":\"org.apache.avro.reflect.TestReflect$\",\"fields\":[]}"); assertEquals(data.getSchema(data.getClass(s)).toString(), - "{\"type\":\"record\",\"name\":\"Z\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[]}"); + "{\"type\":\"record\",\"name\":\"Z\",\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[],\"java-class\":\"org.apache.avro.reflect.TestReflect$Z\"}"); } @Test @@ -1312,7 +1325,7 @@ void avroAliasOnField() { Schema expectedSchema = SchemaBuilder.record(ClassWithAliasOnField.class.getSimpleName()) .namespace("org.apache.avro.reflect.TestReflect").fields().name("primitiveField").aliases("aliasName") .type(Schema.create(org.apache.avro.Schema.Type.INT)).noDefault().endRecord(); - + expectedSchema.addProp("java-class", "org.apache.avro.reflect.TestReflect$ClassWithAliasOnField"); check(ClassWithAliasOnField.class, expectedSchema.toString()); } @@ -1330,7 +1343,7 @@ public void testMultipleFieldAliases() { field.addAlias("alias2"); Schema avroMultiMeta = Schema.createRecord("ClassWithMultipleAliasesOnField", null, "org.apache.avro.reflect.TestReflect", false, Arrays.asList(field)); - + avroMultiMeta.addProp("java-class", "org.apache.avro.reflect.TestReflect$ClassWithMultipleAliasesOnField"); Schema schema = ReflectData.get().getSchema(ClassWithMultipleAliasesOnField.class); assertEquals(avroMultiMeta, schema); } @@ -1344,7 +1357,8 @@ public void testOptional() { check(OptionalTest.class, "{\"type\":\"record\",\"name\":\"OptionalTest\"," + "\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"foo\",\"type\":[\"null\",\"int\"],\"default\":null}]}"); + + "{\"name\":\"foo\",\"type\":[\"null\",\"int\"],\"default\":null}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$OptionalTest\"}"); } private static class DefaultTest { @@ -1357,7 +1371,8 @@ void avroDefault() { check(DefaultTest.class, "{\"type\":\"record\",\"name\":\"DefaultTest\"," + "\"namespace\":\"org.apache.avro.reflect.TestReflect\",\"fields\":[" - + "{\"name\":\"foo\",\"type\":\"int\",\"default\":1}]}"); + + "{\"name\":\"foo\",\"type\":\"int\",\"default\":1}]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$DefaultTest\"}"); } public static class NullableBytesTest { @@ -1407,12 +1422,33 @@ private static class DocTest { void avroDoc() { check(DocTest.class, "{\"type\":\"record\",\"name\":\"DocTest\",\"namespace\":\"org.apache.avro.reflect.TestReflect\"," - + "\"doc\":\"DocTest class docs\"," + "\"fields\":[" - + "{\"name\":\"defaultTest\",\"type\":{\"type\":\"record\",\"name\":\"DefaultTest\"," - + "\"fields\":[{\"name\":\"foo\",\"type\":\"int\",\"default\":1}]},\"doc\":\"And again\"}," - + "{\"name\":\"enums\",\"type\":{\"type\":\"enum\",\"name\":\"DocTestEnum\"," - + "\"symbols\":[\"ENUM_1\",\"ENUM_2\"]},\"doc\":\"Some other Documentation\"}," - + "{\"name\":\"foo\",\"type\":\"int\",\"doc\":\"Some Documentation\"}" + "]}"); + + "\"doc\":\"DocTest class docs\",\"fields\":[{\"name\":\"defaultTest\",\"type\":{\"type\":\"record\"," + + "\"name\":\"DefaultTest\",\"fields\":[{\"name\":\"foo\",\"type\":\"int\",\"default\":1}]," + + "\"java-class\":\"org.apache.avro.reflect.TestReflect$DefaultTest\"},\"doc\":\"And again\"}," + + "{\"name\":\"enums\",\"type\":{\"type\":\"enum\",\"name\":\"DocTestEnum\",\"symbols\":[\"ENUM_1\",\"ENUM_2\"]," + + "\"java-class\":\"org.apache.avro.reflect.TestReflect$DocTestEnum\"},\"doc\":\"Some other Documentation\"}," + + "{\"name\":\"foo\",\"type\":\"int\",\"doc\":\"Some Documentation\"}],\"java-class\":\"org.apache.avro.reflect.TestReflect$DocTest\"}"); + } + + @AvroNamespace("org.apache.avro.reflect.OverrideNamespace") + private static class NamespaceTest { + + private static class InnerNamespaceTest { + } + } + + @Test + void avroOverrideNamespaceTest() { + check(NamespaceTest.class, + "{\"type\":\"record\",\"name\":\"NamespaceTest\",\"namespace\":\"org.apache.avro.reflect.OverrideNamespace\",\"fields\":[]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$NamespaceTest\"}"); + } + + @Test + void avroOverrideEnclosingNamespaceTest() { + check(NamespaceTest.InnerNamespaceTest.class, + "{\"type\":\"record\",\"name\":\"InnerNamespaceTest\",\"namespace\":\"org.apache.avro.reflect.OverrideNamespace\",\"fields\":[]" + + ",\"java-class\":\"org.apache.avro.reflect.TestReflect$NamespaceTest$InnerNamespaceTest\"}"); } } diff --git a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectDatumReader.java b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectDatumReader.java index 52b40b87b36..1c0c2304f53 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectDatumReader.java +++ b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectDatumReader.java @@ -67,6 +67,43 @@ void read_PojoWithList() throws IOException { } + @Test + void testRead_PojoWithCustomNamespace() throws IOException { + PojoWithCustomNamespace pojoWithCustomNamespace = new PojoWithCustomNamespace(); + pojoWithCustomNamespace.setId(42); + + byte[] serializedBytes = serializeWithReflectDatumWriter(pojoWithCustomNamespace, PojoWithCustomNamespace.class); + + Decoder decoder = DecoderFactory.get().binaryDecoder(serializedBytes, null); + ReflectDatumReader reflectDatumReader = new ReflectDatumReader<>( + PojoWithCustomNamespace.class); + + PojoWithCustomNamespace deserialized = new PojoWithCustomNamespace(); + reflectDatumReader.read(deserialized, decoder); + + assertEquals(pojoWithCustomNamespace, deserialized); + + } + + @Test + void testRead_InnerClassPojoWithCustomNamespace() throws IOException { + PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass innerPojoClass = new PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass(); + innerPojoClass.setId(42); + + byte[] serializedBytes = serializeWithReflectDatumWriter(innerPojoClass, + PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass.class); + + Decoder decoder = DecoderFactory.get().binaryDecoder(serializedBytes, null); + ReflectDatumReader reflectDatumReader = new ReflectDatumReader<>( + PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass.class); + + PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass deserialized = new PojoWithInnerClassAndWithCustomNamespace.InnerPojoClass(); + reflectDatumReader.read(deserialized, decoder); + + assertEquals(innerPojoClass, deserialized); + + } + @Test void read_PojoWithArray() throws IOException { PojoWithArray pojoWithArray = new PojoWithArray(); @@ -191,6 +228,75 @@ public void testRead_PojoWithNullableAnnotation() throws IOException { assertEquals(v2Pojo.doubleId, FieldAccess.DOUBLE_DEFAULT_VALUE); } + @AvroNamespace("com.override.sample") + public static class PojoWithCustomNamespace { + private int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PojoWithCustomNamespace other = (PojoWithCustomNamespace) obj; + return id == other.id; + } + } + + @AvroNamespace("com.override.sample") + public static class PojoWithInnerClassAndWithCustomNamespace { + + public static class InnerPojoClass { + private int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + id; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + InnerPojoClass other = (InnerPojoClass) obj; + return id == other.id; + } + } + } + public static class PojoWithList { private int id; private List relatedIds; diff --git a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java index 851ab95e3ea..7dbb7c18a84 100644 --- a/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java +++ b/lang/java/avro/src/test/java/org/apache/avro/reflect/TestReflectLogicalTypes.java @@ -73,6 +73,8 @@ void reflectedSchema() { Schema expected = SchemaBuilder.record(RecordWithUUIDList.class.getName()).fields().name("uuids").type().array() .items().stringType().noDefault().endRecord(); expected.getField("uuids").schema().addProp(SpecificData.CLASS_PROP, List.class.getName()); + expected.addProp("java-class", "org.apache.avro.reflect.RecordWithUUIDList"); + LogicalTypes.uuid().addToSchema(expected.getField("uuids").schema().getElementType()); Schema actual = REFLECT.getSchema(RecordWithUUIDList.class);