From 736999cb8e3ad7c4944b870e39a51260fcf4d73d Mon Sep 17 00:00:00 2001 From: bkniess Date: Fri, 8 Sep 2023 09:34:26 -0400 Subject: [PATCH 1/4] Support enum avro serialization with default value --- avro/pom.xml | 2 +- .../avro/schema/AvroSchemaHelper.java | 12 ++++++- .../jackson/dataformat/avro/AvroTestBase.java | 15 ++++++++ .../avro/schema/SchemaGenerationTest.java | 35 +++++++++++++++++++ 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/avro/pom.xml b/avro/pom.xml index 5a340c5b1..4eea99814 100644 --- a/avro/pom.xml +++ b/avro/pom.xml @@ -47,7 +47,7 @@ abstractions. org.apache.avro avro - 1.8.2 + 1.9.2 diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java index 3d740b17e..12b345e70 100644 --- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes; import com.fasterxml.jackson.databind.util.ClassUtil; @@ -71,6 +72,14 @@ public abstract class AvroSchemaHelper String.class )); + /** + * + * Jackson annotation introspector for verifying enum default annotation for Avro Schema generation + * + * @since 2.16 + */ + private static final JacksonAnnotationIntrospector JACKSON_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector(); + /** * Checks if a given type is "Stringable", that is one of the default * {@code STRINGABLE_CLASSES}, is an {@code Enum}, @@ -269,10 +278,11 @@ public static Schema parseJsonSchema(String json) { */ public static Schema createEnumSchema(BeanDescription bean, List values) { final JavaType enumType = bean.getType(); + Enum defaultEnumValue = JACKSON_ANNOTATION_INTROSPECTOR.findDefaultEnumValue((Class>)(Class) enumType.getRawClass()); return addAlias(Schema.createEnum( getName(enumType), bean.findClassDescription(), - getNamespace(enumType, bean.getClassInfo()), values + getNamespace(enumType, bean.getClassInfo()), values, defaultEnumValue != null ? defaultEnumValue.toString() : null ), bean); } diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/AvroTestBase.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/AvroTestBase.java index 5892a4511..37bebed59 100644 --- a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/AvroTestBase.java +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/AvroTestBase.java @@ -9,6 +9,8 @@ import junit.framework.TestCase; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.fasterxml.jackson.annotation.JsonEnumDefaultValue; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.ObjectMapper; @@ -249,6 +251,19 @@ public Image(String uri, String title, int w, int h, Size s) public enum Size { SMALL, LARGE; } + public enum ABC { + A, + B, + @JsonEnumDefaultValue + C; + } + + public static class ABCDefaultClass { + public String name; + @JsonProperty(required = true) + public ABC abc; + } + /* /********************************************************** /* Recycling for commonly needed helper objects diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java index efd30080b..675eb5509 100644 --- a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.dataformat.avro.*; import org.apache.avro.Schema; +import org.apache.avro.Schema.Type; public class SchemaGenerationTest extends AvroTestBase { @@ -169,4 +170,38 @@ public void testSchemaForUntypedMap() throws Exception verifyException(e, "Maps with non-stringable keys are not supported (yet?)"); } } + + // Issue 388 Default value for enums with class + public void testClassEnumWithDefault() throws Exception + { + AvroSchemaGenerator gen = new AvroSchemaGenerator(); + + MAPPER.acceptJsonFormatVisitor(ABCDefaultClass.class, gen); + AvroSchema schema = gen.getGeneratedSchema(); + assertNotNull(schema); + + String json = schema.getAvroSchema().toString(true); + assertNotNull(json); + + + // And read it back too just for fun + AvroSchema s2 = MAPPER.schemaFrom(json); + assertNotNull(s2); + + Schema avroSchema = s2.getAvroSchema(); + + // String name, int value + assertEquals(Type.RECORD, avroSchema.getType()); + Schema.Field f = avroSchema.getField("abc"); + assertNotNull(f); + assertEquals("abc", f.name()); + + assertEquals(Type.ENUM, f.schema().getType()); + assertEquals(ABC.C.toString(), f.schema().getEnumDefault()); + assertEquals(Stream.of(ABC.values()) + .map(ABC::name) + .collect(Collectors.toList()), f.schema().getEnumSymbols()); + + + } } From 9f799dbfe729902f3eea059962dde0bbaef0feaf Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 10 Sep 2023 18:56:32 -0700 Subject: [PATCH 2/4] Add missing import statements --- .../jackson/dataformat/avro/schema/SchemaGenerationTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java index 675eb5509..688585d80 100644 --- a/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java +++ b/avro/src/test/java/com/fasterxml/jackson/dataformat/avro/schema/SchemaGenerationTest.java @@ -2,6 +2,8 @@ import java.nio.ByteBuffer; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonProperty; From d8c4fea778c0c7f65ae9f6006b15ef0679f754ef Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 10 Sep 2023 18:57:56 -0700 Subject: [PATCH 3/4] Revert Avro version upgrade (code not compatible with 1.9+) --- avro/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avro/pom.xml b/avro/pom.xml index 4eea99814..5a340c5b1 100644 --- a/avro/pom.xml +++ b/avro/pom.xml @@ -47,7 +47,7 @@ abstractions. org.apache.avro avro - 1.9.2 + 1.8.2 From 979070769c2173e3c0ad63bf8a3c8a1a4ccc7320 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Sun, 10 Sep 2023 19:13:03 -0700 Subject: [PATCH 4/4] Remove use of static AnnotationIntrospector --- .../dataformat/avro/schema/AvroSchemaHelper.java | 14 ++++---------- .../dataformat/avro/schema/StringVisitor.java | 3 ++- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java index 12b345e70..9b9f3517b 100644 --- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java @@ -17,6 +17,7 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.cfg.MapperConfig; import com.fasterxml.jackson.databind.introspect.AnnotatedClass; import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor; import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; @@ -72,14 +73,6 @@ public abstract class AvroSchemaHelper String.class )); - /** - * - * Jackson annotation introspector for verifying enum default annotation for Avro Schema generation - * - * @since 2.16 - */ - private static final JacksonAnnotationIntrospector JACKSON_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector(); - /** * Checks if a given type is "Stringable", that is one of the default * {@code STRINGABLE_CLASSES}, is an {@code Enum}, @@ -276,9 +269,10 @@ public static Schema parseJsonSchema(String json) { * @param values List of enum names * @return An {@link org.apache.avro.Schema.Type#ENUM ENUM} schema. */ - public static Schema createEnumSchema(BeanDescription bean, List values) { + public static Schema createEnumSchema(BeanDescription bean, List values, + AnnotationIntrospector intr) { final JavaType enumType = bean.getType(); - Enum defaultEnumValue = JACKSON_ANNOTATION_INTROSPECTOR.findDefaultEnumValue((Class>)(Class) enumType.getRawClass()); + Enum defaultEnumValue = intr.findDefaultEnumValue((Class>)(Class) enumType.getRawClass()); return addAlias(Schema.createEnum( getName(enumType), bean.findClassDescription(), diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/StringVisitor.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/StringVisitor.java index 49946a04f..03da0f2a4 100644 --- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/StringVisitor.java +++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/StringVisitor.java @@ -51,7 +51,8 @@ public Schema builtAvroSchema() { } BeanDescription bean = _provider.getConfig().introspectClassAnnotations(_type); if (_enums != null) { - Schema s = AvroSchemaHelper.createEnumSchema(bean, new ArrayList<>(_enums)); + Schema s = AvroSchemaHelper.createEnumSchema(bean, new ArrayList<>(_enums), + _provider.getAnnotationIntrospector()); _schemas.addSchema(_type, s); return s; }