Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Avro] Fix custom deserializers not being used #71

Merged
merged 1 commit into from
Apr 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@

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.*;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver;
import com.fasterxml.jackson.databind.jsontype.impl.TypeDeserializerBase;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.fasterxml.jackson.dataformat.avro.interop.annotations;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.nio.ByteBuffer;
import java.util.*;

import org.apache.avro.SchemaBuilder;
import org.apache.avro.io.Decoder;
import org.apache.avro.io.Encoder;
import org.apache.avro.reflect.AvroEncode;
Expand Down Expand Up @@ -53,14 +52,17 @@ static class CustomComponent {
@Nullable
public Long longValue;

@AvroEncode(using = UuidAsBytesAvroEncoding.class)
private UUID uuidValue;

protected CustomComponent() { }
}

@SuppressWarnings("unchecked")
public static class ApacheImplEncoding extends CustomEncoding<CustomComponent> {

public ApacheImplEncoding() throws IOException {
schema = ApacheAvroInteropUtil.getJacksonSchema(CustomComponent.class);
schema = ApacheAvroInteropUtil.getApacheSchema(CustomComponent.class);
}

@Override
Expand All @@ -75,8 +77,67 @@ protected CustomComponent read(Object reuse, Decoder in) throws IOException {

}

protected Wrapper wrapper;
public static class UuidAsBytesAvroEncoding extends CustomEncoding<UUID> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original format of this test meant that if the custom deserializer wasn't picked up, Jackson was still flexible enough that it would pass the test because it could read what it wrote. This UuidAsBytesAvroEncoding actually changes the serialized format, which means this will now break if the custom serializers/deserializers are ignored.

public static byte[] asByteArray(UUID uuid) {
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
byte[] buffer = new byte[16];
for (int i = 0; i < 8; i++) {
buffer[i] = (byte) (msb >>> 8 * (7 - i));
}
for (int i = 8; i < 16; i++) {
buffer[i] = (byte) (lsb >>> 8 * (7 - i));
}
return buffer;
}

public static UUID toUUID(byte[] byteArray) {
long msb = 0;
long lsb = 0;
for (int i = 0; i < 8; i++) { msb = (msb << 8) | (byteArray[i] & 0xff); }
for (int i = 8; i < 16; i++) { lsb = (lsb << 8) | (byteArray[i] & 0xff); }
return new UUID(msb, lsb);
}

public UuidAsBytesAvroEncoding() {
this.schema = SchemaBuilder.unionOf().nullType().and().bytesBuilder().endBytes().endUnion();
}

@Override
public void write(Object datum, Encoder encoder) throws IOException {
if (datum == null) {
encoder.writeIndex(0);
encoder.writeNull();
return;
}
encoder.writeIndex(1);
encoder.writeBytes(asByteArray((UUID) datum));
}

@Override
public UUID read(Object datum, Decoder decoder) throws IOException {
try {
// get index in union
int index = decoder.readIndex();
if (index == 1) {
// read in 16 bytes of data
ByteBuffer b = ByteBuffer.allocate(16);
decoder.readBytes(b);
// convert
UUID uuid = toUUID(b.array());
return uuid;
} else {
decoder.readNull();
// no uuid present
return null;
}
} catch (Exception exception) {
throw new IllegalStateException("Could not decode bytes into UUID", exception);
}
}
}

protected Wrapper wrapper;
protected Wrapper result;

@Before
Expand All @@ -93,6 +154,7 @@ public void setup() throws IOException {
mv.put("cats", new ArrayList<Integer>());
mv.put("dogs", new ArrayList<>(Arrays.asList(-1234, 56, 6767, 54134, 57, 86)));
wrapper.component.stringValue = "Hello World!";
wrapper.component.uuidValue = UUID.randomUUID();

CustomComponent cc = new CustomComponent();
cc.byteValue = (byte) 42;
Expand All @@ -101,8 +163,8 @@ public void setup() throws IOException {
cc.doubleValue = Double.POSITIVE_INFINITY;
cc.longValue = Long.MAX_VALUE;
cc.stringValue = "Nested Hello World!";
cc.uuidValue = UUID.randomUUID();
wrapper.component.nestedRecordValue = cc;

//
result = roundTrip(wrapper);
}
Expand Down Expand Up @@ -137,4 +199,13 @@ public void testIntegerValue() {
assertThat(result.component.intValue).isEqualTo(wrapper.component.intValue);
}

@Test
public void testNestedUuidValue() {
assertThat(result.component.nestedRecordValue.uuidValue).isEqualTo(wrapper.component.nestedRecordValue.uuidValue);
}

@Test
public void testUuidValue() {
assertThat(result.component.uuidValue).isEqualTo(wrapper.component.uuidValue);
}
}