Skip to content

Commit c4cf826

Browse files
committed
[Avro] Fix custom deserializers not being used
1 parent 30e7389 commit c4cf826

File tree

4 files changed

+92
-12
lines changed

4 files changed

+92
-12
lines changed

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroAnnotationIntrospector.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
2020
import com.fasterxml.jackson.databind.jsontype.NamedType;
2121
import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
22+
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder;
2223
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
2324
import com.fasterxml.jackson.databind.util.ClassUtil;
2425
import com.fasterxml.jackson.dataformat.avro.deser.CustomEncodingDeserializer;
@@ -161,6 +162,10 @@ public TypeResolverBuilder<?> findPropertyContentTypeResolver(MapperConfig<?> co
161162
}
162163

163164
protected TypeResolverBuilder<?> _findTypeResolver(MapperConfig<?> config, Annotated ann, JavaType baseType) {
165+
// If there's a custom deserializer, then it's responsible for handling any type information
166+
if (config.getAnnotationIntrospector().findDeserializer(ann) != null) {
167+
return StdTypeResolverBuilder.noTypeInfoBuilder();
168+
}
164169
TypeResolverBuilder<?> resolver = new AvroTypeResolverBuilder();
165170
JsonTypeInfo typeInfo = ann.getAnnotation(JsonTypeInfo.class);
166171
if (typeInfo != null && typeInfo.defaultImpl() != JsonTypeInfo.class) {

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/AvroTypeDeserializer.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,7 @@
44

55
import com.fasterxml.jackson.annotation.JsonTypeInfo;
66
import com.fasterxml.jackson.core.JsonParser;
7-
import com.fasterxml.jackson.databind.BeanProperty;
8-
import com.fasterxml.jackson.databind.DeserializationContext;
9-
import com.fasterxml.jackson.databind.JavaType;
10-
import com.fasterxml.jackson.databind.JsonDeserializer;
7+
import com.fasterxml.jackson.databind.*;
118
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
129
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
1310
import com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase;

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/deser/AvroParserImplDecoder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,14 @@ public void skipString() throws IOException {
138138
@Override
139139
public ByteBuffer readBytes(ByteBuffer old) throws IOException {
140140
consumeToken(JsonToken.VALUE_EMBEDDED_OBJECT);
141-
return ByteBuffer.wrap(_parser.getBinaryValue());
141+
byte[] value = _parser.getBinaryValue();
142+
if (old.capacity() >= value.length) {
143+
old.clear();
144+
old.put(value);
145+
old.flip();
146+
return old;
147+
}
148+
return ByteBuffer.wrap(value);
142149
}
143150

144151
@Override

avro/src/test/java/com/fasterxml/jackson/dataformat/avro/interop/annotations/AvroEncodeTest.java

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package com.fasterxml.jackson.dataformat.avro.interop.annotations;
22

33
import java.io.IOException;
4-
import java.util.ArrayList;
5-
import java.util.Arrays;
6-
import java.util.HashMap;
7-
import java.util.Map;
4+
import java.nio.ByteBuffer;
5+
import java.util.*;
86

7+
import org.apache.avro.SchemaBuilder;
98
import org.apache.avro.io.Decoder;
109
import org.apache.avro.io.Encoder;
1110
import org.apache.avro.reflect.AvroEncode;
@@ -53,13 +52,16 @@ static class CustomComponent {
5352
@Nullable
5453
public Long longValue;
5554

55+
@AvroEncode(using = UuidAsBytesAvroEncoding.class)
56+
private UUID uuidValue;
57+
5658
protected CustomComponent() { }
5759
}
5860

5961
public static class ApacheImplEncoding extends CustomEncoding<CustomComponent> {
6062

6163
public ApacheImplEncoding() {
62-
schema = ApacheAvroInteropUtil.getJacksonSchema(CustomComponent.class);
64+
schema = ApacheAvroInteropUtil.getApacheSchema(CustomComponent.class);
6365
}
6466

6567
@Override
@@ -74,8 +76,67 @@ protected CustomComponent read(Object reuse, Decoder in) throws IOException {
7476

7577
}
7678

77-
protected Wrapper wrapper;
79+
public static class UuidAsBytesAvroEncoding extends CustomEncoding<UUID> {
80+
public static byte[] asByteArray(UUID uuid) {
81+
long msb = uuid.getMostSignificantBits();
82+
long lsb = uuid.getLeastSignificantBits();
83+
byte[] buffer = new byte[16];
84+
for (int i = 0; i < 8; i++) {
85+
buffer[i] = (byte) (msb >>> 8 * (7 - i));
86+
}
87+
for (int i = 8; i < 16; i++) {
88+
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
89+
}
90+
return buffer;
91+
}
7892

93+
public static UUID toUUID(byte[] byteArray) {
94+
long msb = 0;
95+
long lsb = 0;
96+
for (int i = 0; i < 8; i++) { msb = (msb << 8) | (byteArray[i] & 0xff); }
97+
for (int i = 8; i < 16; i++) { lsb = (lsb << 8) | (byteArray[i] & 0xff); }
98+
return new UUID(msb, lsb);
99+
}
100+
101+
public UuidAsBytesAvroEncoding() {
102+
this.schema = SchemaBuilder.unionOf().nullType().and().bytesBuilder().endBytes().endUnion();
103+
}
104+
105+
@Override
106+
public void write(Object datum, Encoder encoder) throws IOException {
107+
if (datum == null) {
108+
encoder.writeIndex(0);
109+
encoder.writeNull();
110+
return;
111+
}
112+
encoder.writeIndex(1);
113+
encoder.writeBytes(asByteArray((UUID) datum));
114+
}
115+
116+
@Override
117+
public UUID read(Object datum, Decoder decoder) throws IOException {
118+
try {
119+
// get index in union
120+
int index = decoder.readIndex();
121+
if (index == 1) {
122+
// read in 16 bytes of data
123+
ByteBuffer b = ByteBuffer.allocate(16);
124+
decoder.readBytes(b);
125+
// convert
126+
UUID uuid = toUUID(b.array());
127+
return uuid;
128+
} else {
129+
decoder.readNull();
130+
// no uuid present
131+
return null;
132+
}
133+
} catch (Exception exception) {
134+
throw new IllegalStateException("Could not decode bytes into UUID", exception);
135+
}
136+
}
137+
}
138+
139+
protected Wrapper wrapper;
79140
protected Wrapper result;
80141

81142
@Before
@@ -92,6 +153,7 @@ public void setup() throws IOException {
92153
mv.put("cats", new ArrayList<Integer>());
93154
mv.put("dogs", new ArrayList<>(Arrays.asList(-1234, 56, 6767, 54134, 57, 86)));
94155
wrapper.component.stringValue = "Hello World!";
156+
wrapper.component.uuidValue = UUID.randomUUID();
95157

96158
CustomComponent cc = new CustomComponent();
97159
cc.byteValue = (byte) 42;
@@ -100,8 +162,8 @@ public void setup() throws IOException {
100162
cc.doubleValue = Double.POSITIVE_INFINITY;
101163
cc.longValue = Long.MAX_VALUE;
102164
cc.stringValue = "Nested Hello World!";
165+
cc.uuidValue = UUID.randomUUID();
103166
wrapper.component.nestedRecordValue = cc;
104-
105167
//
106168
result = roundTrip(wrapper);
107169
}
@@ -136,4 +198,13 @@ public void testIntegerValue() {
136198
assertThat(result.component.intValue).isEqualTo(wrapper.component.intValue);
137199
}
138200

201+
@Test
202+
public void testNestedUuidValue() {
203+
assertThat(result.component.nestedRecordValue.uuidValue).isEqualTo(wrapper.component.nestedRecordValue.uuidValue);
204+
}
205+
206+
@Test
207+
public void testUuidValue() {
208+
assertThat(result.component.uuidValue).isEqualTo(wrapper.component.uuidValue);
209+
}
139210
}

0 commit comments

Comments
 (0)