Skip to content

Commit 45040df

Browse files
committed
Last refactorings
1 parent 9dc2af8 commit 45040df

File tree

7 files changed

+145
-93
lines changed

7 files changed

+145
-93
lines changed

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

+5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
* <ul>
1616
* <li>{@link AvroIgnore @AvroIgnore} - Alias for <code>JsonIgnore</code></li>
1717
* <li>{@link AvroName @AvroName("custom Name")} - Alias for <code>JsonProperty("custom name")</code></li>
18+
* <li>{@link AvroDefault @AvroDefault("default value")} - Alias for <code>JsonProperty.defaultValue</code>, to
19+
* define default value for generated Schemas
20+
* </li>
1821
* </ul>
22+
*
23+
* @since 2.9
1924
*/
2025
public class AvroAnnotationIntrospector extends AnnotationIntrospector
2126
{

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

+32-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
import org.apache.avro.Schema;
66

77
import com.fasterxml.jackson.core.Version;
8+
89
import com.fasterxml.jackson.databind.JavaType;
910
import com.fasterxml.jackson.databind.JsonMappingException;
11+
import com.fasterxml.jackson.databind.Module;
1012
import com.fasterxml.jackson.databind.ObjectMapper;
13+
1114
import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaGenerator;
1215

1316
/**
@@ -21,19 +24,47 @@ public class AvroMapper extends ObjectMapper
2124
{
2225
private static final long serialVersionUID = 1L;
2326

27+
/**
28+
* Constructor that will construct mapper with standard {@link AvroFactory}
29+
* as codec, and will also register {@link AvroModule}.
30+
*/
2431
public AvroMapper() {
2532
this(new AvroFactory());
2633
}
2734

35+
/**
36+
* Constructor that will construct mapper with given {@link AvroFactory},
37+
* as well as register standard {@link AvroModule} (with default settings).
38+
*/
2839
public AvroMapper(AvroFactory f) {
2940
super(f);
3041
registerModule(new AvroModule());
3142
}
3243

44+
/**
45+
* Constructor that will construct mapper with standard {@link AvroFactory}
46+
* as codec, and register given modules but nothing else (that is, will
47+
* only register {@link AvroModule} if it's included as argument.
48+
*/
49+
public AvroMapper(Module... modules) {
50+
super(new AvroFactory());
51+
registerModules(modules);
52+
}
53+
54+
/**
55+
* Constructor that will construct mapper with specified {@link AvroFactory}
56+
* as codec, and register given modules but nothing else (that is, will
57+
* only register {@link AvroModule} if it's included as argument.
58+
*/
59+
public AvroMapper(AvroFactory f, Module... modules) {
60+
super(f);
61+
registerModules(modules);
62+
}
63+
3364
protected AvroMapper(ObjectMapper src) {
3465
super(src);
3566
}
36-
67+
3768
@Override
3869
public AvroMapper copy()
3970
{

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

+20-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ public class AvroModule extends SimpleModule
1919
{
2020
private static final long serialVersionUID = 1L;
2121

22+
protected final static AvroAnnotationIntrospector INTR
23+
= new AvroAnnotationIntrospector();
24+
25+
/**
26+
* @since 2.8.7
27+
*/
28+
protected boolean _cfgAddIntrospector = true;
29+
2230
public AvroModule()
2331
{
2432
super(PackageVersion.VERSION);
@@ -36,7 +44,18 @@ public AvroModule()
3644
@Override
3745
public void setupModule(SetupContext context) {
3846
super.setupModule(context);
39-
context.insertAnnotationIntrospector(new AvroAnnotationIntrospector());
47+
if (_cfgAddIntrospector) {
48+
// insert (instead of append) to have higher precedence
49+
context.insertAnnotationIntrospector(INTR);
50+
}
51+
}
52+
53+
/**
54+
* @since 2.8.7
55+
*/
56+
public AvroModule withAnnotationIntrospector(boolean state) {
57+
_cfgAddIntrospector = state;
58+
return this;
4059
}
4160

4261
/*

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/schema/AvroSchemaHelper.java

+14-27
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
1717
import com.fasterxml.jackson.databind.introspect.AnnotatedConstructor;
1818
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
19+
import com.fasterxml.jackson.databind.util.ClassUtil;
1920

2021
public abstract class AvroSchemaHelper
2122
{
@@ -26,30 +27,31 @@ public abstract class AvroSchemaHelper
2627
*
2728
* @since 2.8.7
2829
*/
29-
public static final String AVRO_SCHEMA_PROP_CLASS = SpecificData.CLASS_PROP;
30+
public static final String AVRO_SCHEMA_PROP_CLASS = SpecificData.CLASS_PROP;
31+
3032
/**
3133
* Constant used by native Avro Schemas for indicating more specific
3234
* physical class of a map key; referenced indirectly to reduce direct
3335
* dependencies to the standard avro library.
3436
*
3537
* @since 2.8.7
3638
*/
37-
public static final String AVRO_SCHEMA_PROP_KEY_CLASS = SpecificData.KEY_CLASS_PROP;
39+
public static final String AVRO_SCHEMA_PROP_KEY_CLASS = SpecificData.KEY_CLASS_PROP;
40+
3841
/**
3942
* Default stringable classes
4043
*
4144
* @since 2.8.7
4245
*/
43-
protected static final Set<Class<?>> STRINGABLE_CLASSES = new HashSet<Class<?>>(Arrays.asList(URI.class,
44-
URL.class,
45-
File.class,
46-
BigInteger.class,
47-
BigDecimal.class,
48-
String.class
46+
protected static final Set<Class<?>> STRINGABLE_CLASSES = new HashSet<Class<?>>(Arrays.asList(
47+
URI.class, URL.class, File.class,
48+
BigInteger.class, BigDecimal.class,
49+
String.class
4950
));
5051

5152
/**
52-
* Checks if a given type is "Stringable", that is one of the default {@link #STRINGABLE_CLASSES}, or is annotated with
53+
* Checks if a given type is "Stringable", that is one of the default {@link #STRINGABLE_CLASSES},
54+
* or is annotated with
5355
* {@link Stringable @Stringable} and has a constructor that takes a single string argument capable of deserializing the output of its
5456
* {@code toString()} method.
5557
*
@@ -191,24 +193,9 @@ protected static <T> T throwUnsupported() {
191193
*/
192194
protected static String getTypeId(JavaType type) {
193195
// Primitives use the name of the wrapper class as their type ID
194-
String canonical = type.toCanonical();
195-
switch (canonical) {
196-
case "byte":
197-
return Byte.class.getName();
198-
case "short":
199-
return Short.class.getName();
200-
case "char":
201-
return Character.class.getName();
202-
case "int":
203-
return Integer.class.getName();
204-
case "long":
205-
return Long.class.getName();
206-
case "float":
207-
return Float.class.getName();
208-
case "double":
209-
return Double.class.getName();
210-
default:
211-
return canonical;
196+
if (type.isPrimitive()) {
197+
return ClassUtil.wrapperType(type.getRawClass()).getName();
212198
}
199+
return type.toCanonical();
213200
}
214201
}

avro/src/main/java/com/fasterxml/jackson/dataformat/avro/ser/NonBSGenericDatumWriter.java

+67-56
Original file line numberDiff line numberDiff line change
@@ -25,61 +25,72 @@ public NonBSGenericDatumWriter(Schema root) {
2525
super(root);
2626
}
2727

28-
@Override
29-
public int resolveUnion(Schema union, Object datum) {
30-
// Alas, we need a work-around first...
31-
if (datum == null) {
32-
return union.getIndexNamed(Type.NULL.getName());
33-
}
34-
List<Schema> schemas = union.getTypes();
35-
for (int i = 0, len = schemas.size(); i < len; i++) {
36-
Schema s = schemas.get(i);
37-
if (datum instanceof BigDecimal && s.getType() == Type.DOUBLE) {
38-
return i;
39-
}
40-
if (datum instanceof String) { // String or Enum or Character or char[]
41-
switch (s.getType()) {
42-
case STRING:
43-
case ENUM:
44-
return i;
45-
case INT:
46-
// Avro distinguishes between String and Character, whereas Jackson doesn't
47-
// Check if the schema is expecting a Character and handle appropriately
48-
if (Character.class.getName().equals(s.getProp(AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS))) {
49-
return i;
50-
}
51-
break;
52-
case ARRAY:
53-
// Avro distinguishes between String and char[], whereas Jackson doesn't
54-
// Check if the schema is expecting a char[] and handle appropriately
55-
if (s.getElementType().getType() == Type.INT && Character.class
56-
.getName().equals(s.getElementType().getProp(AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS))) {
57-
return i;
58-
}
59-
break;
60-
default:
61-
}
62-
}
63-
}
64-
// otherwise just default to base impl, stupid as it is...
65-
return super.resolveUnion(union, datum);
66-
}
28+
@Override
29+
public int resolveUnion(Schema union, Object datum) {
30+
// Alas, we need a work-around first...
31+
if (datum == null) {
32+
return union.getIndexNamed(Type.NULL.getName());
33+
}
34+
List<Schema> schemas = union.getTypes();
35+
if (datum instanceof String) { // String or Enum or Character or char[]
36+
for (int i = 0, len = schemas.size(); i < len; i++) {
37+
Schema s = schemas.get(i);
38+
switch (s.getType()) {
39+
case STRING:
40+
case ENUM:
41+
return i;
42+
case INT:
43+
// Avro distinguishes between String and Character, whereas Jackson doesn't
44+
// Check if the schema is expecting a Character and handle appropriately
45+
if (Character.class.getName().equals(s.getProp(AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS))) {
46+
return i;
47+
}
48+
break;
49+
case ARRAY:
50+
// Avro distinguishes between String and char[], whereas Jackson doesn't
51+
// Check if the schema is expecting a char[] and handle appropriately
52+
if (s.getElementType().getType() == Type.INT && Character.class
53+
.getName().equals(s.getElementType().getProp(AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS))) {
54+
return i;
55+
}
56+
break;
57+
default:
58+
}
59+
}
60+
} else if (datum instanceof BigDecimal) {
61+
for (int i = 0, len = schemas.size(); i < len; i++) {
62+
if (schemas.get(i).getType() == Type.DOUBLE) {
63+
return i;
64+
}
65+
}
66+
}
6767

68-
@Override
69-
protected void write(Schema schema, Object datum, Encoder out) throws IOException {
70-
if ((schema.getType() == Type.DOUBLE) && datum instanceof BigDecimal) {
71-
out.writeDouble(((BigDecimal)datum).doubleValue());
72-
} else if (datum instanceof String && schema.getType() == Type.ARRAY && schema.getElementType().getType() == Type.INT) {
73-
ArrayList<Integer> chars = new ArrayList<>(((String) datum).length());
74-
char[] src = ((String) datum).toCharArray();
75-
for (int i = 0; i < src.length; i++) {
76-
chars.add((int) src[i]);
77-
}
78-
super.write(schema, chars, out);
79-
} else if (datum instanceof String && ((String) datum).length() == 1 && schema.getType() == Type.INT) {
80-
super.write(schema, (int) ((String) datum).charAt(0), out);
81-
} else {
82-
super.write(schema, datum, out);
83-
}
84-
}
68+
// otherwise just default to base impl, stupid as it is...
69+
return super.resolveUnion(union, datum);
70+
}
71+
72+
@Override
73+
protected void write(Schema schema, Object datum, Encoder out) throws IOException {
74+
if ((schema.getType() == Type.DOUBLE) && datum instanceof BigDecimal) {
75+
out.writeDouble(((BigDecimal)datum).doubleValue());
76+
return;
77+
}
78+
if (datum instanceof String) {
79+
String str = (String) datum;
80+
final int len = str.length();
81+
if (schema.getType() == Type.ARRAY && schema.getElementType().getType() == Type.INT) {
82+
ArrayList<Integer> chars = new ArrayList<>(len);
83+
for (int i = 0; i < len; ++i) {
84+
chars.add((int) str.charAt(i));
85+
}
86+
super.write(schema, chars, out);
87+
return;
88+
}
89+
if (len == 1 && schema.getType() == Type.INT) {
90+
super.write(schema, (int) str.charAt(0), out);
91+
return;
92+
}
93+
}
94+
super.write(schema, datum, out);
95+
}
8596
}

avro/src/test/java/com/fasterxml/jackson/dataformat/avro/interop/ApacheAvroInteropUtil.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.JavaType;
2020
import com.fasterxml.jackson.databind.JsonMappingException;
2121
import com.fasterxml.jackson.dataformat.avro.AvroMapper;
22+
import com.fasterxml.jackson.dataformat.avro.AvroModule;
2223
import com.fasterxml.jackson.dataformat.avro.AvroSchema;
2324

2425
/**
@@ -80,7 +81,9 @@ public byte[] apply(Schema schema, Object originalObject) {
8081
return apacheSerialize(schema, originalObject);
8182
}
8283
};
83-
private static final AvroMapper MAPPER = new AvroMapper();
84+
85+
private static final AvroMapper MAPPER = new AvroMapper(new AvroModule());
86+
8487
/*
8588
* Special subclass of ReflectData that knows how to resolve and bind generic types. This saves us much pain of
8689
* having to build these schemas by hand. Also, workarounds for several bugs in the Apache implementation are
@@ -236,9 +239,8 @@ public static Schema getJacksonSchema(Type type) {
236239
* @return Deserialized payload
237240
*/
238241
public static <T> T jacksonDeserialize(Schema schema, JavaType type, byte[] data) {
239-
AvroMapper mapper = new AvroMapper();
240242
try {
241-
return mapper.readerFor(type).with(new AvroSchema(schema)).readValue(data, 0, data.length);
243+
return MAPPER.readerFor(type).with(new AvroSchema(schema)).readValue(data, 0, data.length);
242244
} catch (IOException e) {
243245
throw new RuntimeException("Failed to Deserialize", e);
244246
}
@@ -273,10 +275,9 @@ public static <T> T jacksonDeserialize(Schema schema, Type type, byte[] data) {
273275
* @return Payload containing the Avro-serialized form of {@code object}
274276
*/
275277
public static byte[] jacksonSerialize(Schema schema, Object object) {
276-
AvroMapper mapper = new AvroMapper();
277278
try {
278-
return mapper.writer().with(new AvroSchema(schema)).writeValueAsBytes(object);
279-
} catch (JsonProcessingException e) {
279+
return MAPPER.writer().with(new AvroSchema(schema)).writeValueAsBytes(object);
280+
} catch (IOException e) {
280281
throw new RuntimeException("Failed Serialization", e);
281282
}
282283
}

cbor/src/test/java/com/fasterxml/jackson/dataformat/cbor/SharedRawGeneratorBufferTest.java

-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
import com.fasterxml.jackson.databind.*;
1010

11-
import com.fasterxml.jackson.dataformat.cbor.*;
12-
1311
// for [dataformats-binary#43]
1412
public class SharedRawGeneratorBufferTest extends CBORTestBase
1513
{

0 commit comments

Comments
 (0)