diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroAnnotationIntrospector.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroAnnotationIntrospector.java
index 65f5c164b..d52551ea2 100644
--- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroAnnotationIntrospector.java
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroAnnotationIntrospector.java
@@ -1,20 +1,27 @@
package com.fasterxml.jackson.dataformat.avro;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.avro.reflect.*;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.AnnotationIntrospector;
+import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.PropertyName;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
+import com.fasterxml.jackson.databind.jsontype.NamedType;
+import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaHelper;
+
/**
* Adds support for the following annotations from the Apache Avro implementation:
*
@@ -26,6 +33,7 @@
* - {@link Nullable @Nullable} - Alias for
JsonProperty(required = false)
* - {@link Stringable @Stringable} - Alias for
JsonCreator
on the constructor and JsonValue
on
* the {@link #toString()} method.
+ * - {@link Union @Union} - Alias for
JsonSubTypes
*
*
* @since 2.9
@@ -70,7 +78,7 @@ public List findPropertyAliases(Annotated m) {
}
protected PropertyName _findName(Annotated a)
- {
+ {
AvroName ann = _findAnnotation(a, AvroName.class);
return (ann == null) ? null : PropertyName.construct(ann.value());
}
@@ -107,4 +115,41 @@ public Object findSerializer(Annotated a) {
}
return null;
}
+
+ @Override
+ public List findSubtypes(Annotated a) {
+ Union union = _findAnnotation(a, Union.class);
+ if (union == null) {
+ return null;
+ }
+ ArrayList names = new ArrayList<>(union.value().length);
+ for (Class> subtype : union.value()) {
+ names.add(new NamedType(subtype, AvroSchemaHelper.getTypeId(subtype)));
+ }
+ return names;
+ }
+
+ @Override
+ public TypeResolverBuilder> findTypeResolver(MapperConfig> config, AnnotatedClass ac, JavaType baseType) {
+ return _findTypeResolver(config, ac, baseType);
+ }
+
+ @Override
+ public TypeResolverBuilder> findPropertyTypeResolver(MapperConfig> config, AnnotatedMember am, JavaType baseType) {
+ return _findTypeResolver(config, am, baseType);
+ }
+
+ @Override
+ public TypeResolverBuilder> findPropertyContentTypeResolver(MapperConfig> config, AnnotatedMember am, JavaType containerType) {
+ return _findTypeResolver(config, am, containerType);
+ }
+
+ protected TypeResolverBuilder> _findTypeResolver(MapperConfig> config, Annotated ann, JavaType baseType) {
+ TypeResolverBuilder> resolver = new AvroTypeResolverBuilder();
+ JsonTypeInfo typeInfo = ann.getAnnotation(JsonTypeInfo.class);
+ if (typeInfo != null && typeInfo.defaultImpl() != JsonTypeInfo.class) {
+ resolver = resolver.defaultImpl(typeInfo.defaultImpl());
+ }
+ return resolver;
+ }
}
diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroGenerator.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroGenerator.java
index 00c320bc7..381de6a7d 100644
--- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroGenerator.java
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroGenerator.java
@@ -1,18 +1,26 @@
package com.fasterxml.jackson.dataformat.avro;
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.base.GeneratorBase;
-import com.fasterxml.jackson.core.io.IOContext;
-import com.fasterxml.jackson.dataformat.avro.ser.AvroWriteContext;
-
-import org.apache.avro.io.BinaryEncoder;
-
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
+import org.apache.avro.io.BinaryEncoder;
+
+import com.fasterxml.jackson.core.Base64Variant;
+import com.fasterxml.jackson.core.FormatFeature;
+import com.fasterxml.jackson.core.FormatSchema;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.core.PrettyPrinter;
+import com.fasterxml.jackson.core.SerializableString;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.base.GeneratorBase;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.dataformat.avro.ser.AvroWriteContext;
+
public class AvroGenerator extends GeneratorBase
{
/**
@@ -381,6 +389,17 @@ public final void writeStartObject() throws IOException {
_complete = false;
}
+ @Override
+ public void writeStartObject(Object forValue) throws IOException {
+ _avroContext = _avroContext.createChildObjectContext(forValue);
+ _complete = false;
+ if(this._writeContext != null && forValue != null) {
+ this._writeContext.setCurrentValue(forValue);
+ }
+
+ this.setCurrentValue(forValue);
+ }
+
@Override
public final void writeEndObject() throws IOException
{
diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroMapper.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroMapper.java
index 1d301e6af..c9f4e68c7 100644
--- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroMapper.java
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroMapper.java
@@ -1,16 +1,16 @@
package com.fasterxml.jackson.dataformat.avro;
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import org.apache.avro.Schema;
import com.fasterxml.jackson.core.Version;
-
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
-
import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaGenerator;
/**
diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroModule.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroModule.java
index b071517ed..e18eca0da 100644
--- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroModule.java
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroModule.java
@@ -37,6 +37,8 @@ public AvroModule()
addSerializer(File.class, new ToStringSerializer(File.class));
// 08-Mar-2016, tatu: to fix [dataformat-avro#35], need to prune 'schema' property:
setSerializerModifier(new AvroSerializerModifier());
+ // Override untyped deserializer to one that checks for type information in the schema before going to default handling
+ addDeserializer(Object.class, new AvroUntypedDeserializer());
}
@Override
diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroParser.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroParser.java
index 34f6a3a38..8a7e026ab 100644
--- a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroParser.java
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroParser.java
@@ -1,7 +1,6 @@
package com.fasterxml.jackson.dataformat.avro;
import java.io.IOException;
-import java.io.InputStream;
import java.io.Writer;
import java.math.BigDecimal;
@@ -229,6 +228,16 @@ public void setSchema(FormatSchema schema)
protected abstract void _initSchema(AvroSchema schema) throws JsonProcessingException;
+ @Override
+ public boolean canReadTypeId() {
+ return true;
+ }
+
+ @Override
+ public Object getTypeId() throws IOException {
+ return _avroContext != null ? _avroContext.getTypeId() : null;
+ }
+
/*
/**********************************************************
/* Location info
diff --git a/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroTypeDeserializer.java b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroTypeDeserializer.java
new file mode 100644
index 000000000..cb987d1b1
--- /dev/null
+++ b/avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroTypeDeserializer.java
@@ -0,0 +1,74 @@
+package com.fasterxml.jackson.dataformat.avro;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
+import com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase;
+import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaHelper;
+
+public class AvroTypeDeserializer extends TypeDeserializerBase {
+
+ protected AvroTypeDeserializer(JavaType baseType, TypeIdResolver idRes, String typePropertyName, boolean typeIdVisible,
+ JavaType defaultImpl) {
+ super(baseType, idRes, typePropertyName, typeIdVisible, defaultImpl);
+ }
+
+ protected AvroTypeDeserializer(TypeDeserializerBase src, BeanProperty property) {
+ super(src, property);
+ }
+
+ @Override
+ public TypeDeserializer forProperty(BeanProperty prop) {
+ return new AvroTypeDeserializer(this, prop);
+ }
+
+ @Override
+ public JsonTypeInfo.As getTypeInclusion() {
+ // Don't do any restructuring of the incoming JSON tokens
+ return JsonTypeInfo.As.EXISTING_PROPERTY;
+ }
+
+ @Override
+ public Object deserializeTypedFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return deserializeTypedFromAny(p, ctxt);
+ }
+
+ @Override
+ public Object deserializeTypedFromArray(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return deserializeTypedFromAny(p, ctxt);
+ }
+
+ @Override
+ public Object deserializeTypedFromScalar(JsonParser p, DeserializationContext ctxt) throws IOException {
+ return deserializeTypedFromAny(p, ctxt);
+ }
+
+ @Override
+ public Object deserializeTypedFromAny(JsonParser p, DeserializationContext ctxt) throws IOException {
+ if (p.getTypeId() == null && getDefaultImpl() == null) {
+ JsonDeserializer