Skip to content

Commit 79dad0a

Browse files
committed
Merge branch '2.8'
2 parents f391fac + 45040df commit 79dad0a

24 files changed

+1729
-64
lines changed

avro/pom.xml

+24
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,30 @@ abstractions.
3838
<artifactId>jackson-annotations</artifactId>
3939
<scope>test</scope>
4040
</dependency>
41+
42+
<!-- plus logback -->
43+
<dependency>
44+
<groupId>ch.qos.logback</groupId>
45+
<artifactId>logback-classic</artifactId>
46+
<version>1.1.3</version>
47+
<scope>test</scope>
48+
</dependency>
49+
<!-- A bit of help to reduce boiler-plate in dummy test classes -->
50+
<dependency>
51+
<groupId>org.projectlombok</groupId>
52+
<artifactId>lombok</artifactId>
53+
<version>1.16.14</version>
54+
<scope>test</scope>
55+
</dependency>
56+
<!-- For validating more complex comparisons -->
57+
<!-- 27-Feb-2017, tatu: NOTE! Can NOT use 3.x as it requires Java 8
58+
-->
59+
<dependency>
60+
<groupId>org.assertj</groupId>
61+
<artifactId>assertj-core</artifactId>
62+
<version>2.5.0</version>
63+
<scope>test</scope>
64+
</dependency>
4165
</dependencies>
4266

4367
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.fasterxml.jackson.dataformat.avro;
2+
3+
import com.fasterxml.jackson.core.Version;
4+
import com.fasterxml.jackson.databind.AnnotationIntrospector;
5+
import com.fasterxml.jackson.databind.PropertyName;
6+
import com.fasterxml.jackson.databind.introspect.Annotated;
7+
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
8+
9+
import org.apache.avro.reflect.AvroDefault;
10+
import org.apache.avro.reflect.AvroIgnore;
11+
import org.apache.avro.reflect.AvroName;
12+
13+
/**
14+
* Adds support for the following annotations from the Apache Avro implementation:
15+
* <ul>
16+
* <li>{@link AvroIgnore @AvroIgnore} - Alias for <code>JsonIgnore</code></li>
17+
* <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>
21+
* </ul>
22+
*
23+
* @since 2.9
24+
*/
25+
public class AvroAnnotationIntrospector extends AnnotationIntrospector
26+
{
27+
private static final long serialVersionUID = 1L;
28+
29+
@Override
30+
public Version version() {
31+
return PackageVersion.VERSION;
32+
}
33+
34+
@Override
35+
public boolean hasIgnoreMarker(AnnotatedMember m) {
36+
return _findAnnotation(m, AvroIgnore.class) != null;
37+
}
38+
39+
@Override
40+
public PropertyName findNameForSerialization(Annotated a) {
41+
return _findName(a);
42+
}
43+
44+
@Override
45+
public PropertyName findNameForDeserialization(Annotated a) {
46+
return _findName(a);
47+
}
48+
49+
@Override
50+
public String findPropertyDefaultValue(Annotated m) {
51+
AvroDefault ann = _findAnnotation(m, AvroDefault.class);
52+
return (ann == null) ? null : ann.value();
53+
}
54+
55+
protected PropertyName _findName(Annotated a)
56+
{
57+
AvroName ann = _findAnnotation(a, AvroName.class);
58+
return (ann == null) ? null : PropertyName.construct(ann.value());
59+
}
60+
}

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

+27-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44

55
import org.apache.avro.Schema;
66

7-
import com.fasterxml.jackson.core.*;
8-
9-
import com.fasterxml.jackson.databind.*;
7+
import com.fasterxml.jackson.core.JsonGenerator;
8+
import com.fasterxml.jackson.databind.SerializerProvider;
109
import com.fasterxml.jackson.databind.module.SimpleModule;
1110
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
1211

@@ -20,6 +19,14 @@ public class AvroModule extends SimpleModule
2019
{
2120
private static final long serialVersionUID = 1L;
2221

22+
protected final static AvroAnnotationIntrospector INTR
23+
= new AvroAnnotationIntrospector();
24+
25+
/**
26+
* @since 2.9
27+
*/
28+
protected boolean _cfgAddIntrospector = true;
29+
2330
public AvroModule()
2431
{
2532
super(PackageVersion.VERSION);
@@ -28,6 +35,23 @@ public AvroModule()
2835
setSerializerModifier(new AvroSerializerModifier());
2936
}
3037

38+
@Override
39+
public void setupModule(SetupContext context) {
40+
super.setupModule(context);
41+
if (_cfgAddIntrospector) {
42+
// insert (instead of append) to have higher precedence
43+
context.insertAnnotationIntrospector(INTR);
44+
}
45+
}
46+
47+
/**
48+
* @since 2.9
49+
*/
50+
public AvroModule withAnnotationIntrospector(boolean state) {
51+
_cfgAddIntrospector = state;
52+
return this;
53+
}
54+
3155
/*
3256
/**********************************************************
3357
/* Helper classes (as long as number is small)

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

+7-3
Original file line numberDiff line numberDiff line change
@@ -206,18 +206,22 @@ public void skipBoolean() throws IOException {
206206
_decoder.skipFixed(1);
207207
}
208208

209-
public JsonToken decodeInt() throws IOException {
209+
public int decodeInt() throws IOException {
210+
return _decoder.readInt();
211+
}
212+
213+
public JsonToken decodeIntToken() throws IOException {
210214
_numberInt = _decoder.readInt();
211215
_numTypesValid = NR_INT;
212216
return JsonToken.VALUE_NUMBER_INT;
213217
}
214-
218+
215219
public void skipInt() throws IOException {
216220
// ints use variable-length zigzagging; alas, no native skipping
217221
_decoder.readInt();
218222
}
219223

220-
public JsonToken decodeLong() throws IOException {
224+
public JsonToken decodeLongToken() throws IOException {
221225
_numberLong = _decoder.readLong();
222226
_numTypesValid = NR_LONG;
223227
return JsonToken.VALUE_NUMBER_INT;

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

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.apache.avro.Schema;
66

77
import com.fasterxml.jackson.dataformat.avro.deser.ScalarDecoder.*;
8+
import com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaHelper;
89

910
/**
1011
* Helper class used for constructing a hierarchic reader for given
@@ -20,6 +21,7 @@ public abstract class AvroReaderFactory
2021
protected final static ScalarDecoder READER_LONG = new LongReader();
2122
protected final static ScalarDecoder READER_NULL = new NullReader();
2223
protected final static ScalarDecoder READER_STRING = new StringReader();
24+
protected final static ScalarDecoder READER_CHAR = new CharReader();
2325

2426
/**
2527
* To resolve cyclic types, need to keep track of resolved named
@@ -65,6 +67,9 @@ public ScalarDecoder createScalarValueDecoder(Schema type)
6567
case FLOAT:
6668
return READER_FLOAT;
6769
case INT:
70+
if (Character.class.getName().equals(type.getProp(AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS))) {
71+
return READER_CHAR;
72+
}
6873
return READER_INT;
6974
case LONG:
7075
return READER_LONG;

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

+39-4
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ protected final static class IntReader extends ScalarDecoder
131131
{
132132
@Override
133133
public JsonToken decodeValue(AvroParserImpl parser) throws IOException {
134-
return parser.decodeInt();
134+
return parser.decodeIntToken();
135135
}
136136

137137
@Override
@@ -151,7 +151,7 @@ public FR(String name, boolean skipper) {
151151

152152
@Override
153153
public JsonToken readValue(AvroReadContext parent, AvroParserImpl parser) throws IOException {
154-
return parser.decodeInt();
154+
return parser.decodeIntToken();
155155
}
156156

157157
@Override
@@ -165,7 +165,7 @@ protected final static class LongReader extends ScalarDecoder
165165
{
166166
@Override
167167
public JsonToken decodeValue(AvroParserImpl parser) throws IOException {
168-
return parser.decodeLong();
168+
return parser.decodeLongToken();
169169
}
170170

171171
@Override
@@ -185,7 +185,7 @@ public FR(String name, boolean skipper) {
185185

186186
@Override
187187
public JsonToken readValue(AvroReadContext parent, AvroParserImpl parser) throws IOException {
188-
return parser.decodeLong();
188+
return parser.decodeLongToken();
189189
}
190190

191191
@Override
@@ -194,6 +194,41 @@ public void skipValue(AvroParserImpl parser) throws IOException {
194194
}
195195
}
196196
}
197+
198+
protected final static class CharReader extends ScalarDecoder {
199+
@Override
200+
public JsonToken decodeValue(AvroParserImpl parser) throws IOException {
201+
return parser.setString(Character.toString((char)parser.decodeInt()));
202+
}
203+
204+
@Override
205+
protected void skipValue(AvroParserImpl parser) throws IOException {
206+
// ints use variable-length zigzagging; alas, no native skipping
207+
parser.skipInt();
208+
}
209+
210+
@Override
211+
public AvroFieldReader asFieldReader(String name, boolean skipper) {
212+
return new FR(name, skipper);
213+
}
214+
215+
private final static class FR extends AvroFieldReader {
216+
public FR(String name, boolean skipper) {
217+
super(name, skipper);
218+
}
219+
220+
@Override
221+
public JsonToken readValue(AvroReadContext parent, AvroParserImpl parser)
222+
throws IOException {
223+
return parser.setString(Character.toString((char) parser.decodeInt()));
224+
}
225+
226+
@Override
227+
public void skipValue(AvroParserImpl parser) throws IOException {
228+
parser.skipInt();
229+
}
230+
}
231+
}
197232

198233
protected final static class NullReader extends ScalarDecoder
199234
{

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

+18-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package com.fasterxml.jackson.dataformat.avro.schema;
22

33
import org.apache.avro.Schema;
4+
import org.apache.avro.Schema.Type;
45

5-
import com.fasterxml.jackson.databind.*;
6-
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.JsonMappingException;
8+
import com.fasterxml.jackson.databind.SerializerProvider;
79
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
10+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes;
811
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable;
912

13+
import static com.fasterxml.jackson.dataformat.avro.schema.AvroSchemaHelper.AVRO_SCHEMA_PROP_CLASS;
14+
1015
public class ArrayVisitor
1116
extends JsonArrayFormatVisitor.Base
1217
implements SchemaBuilder
@@ -29,7 +34,11 @@ public Schema builtAvroSchema() {
2934
if (_elementSchema == null) {
3035
throw new IllegalStateException("No element schema created for: "+_type);
3136
}
32-
return Schema.createArray(_elementSchema);
37+
Schema schema = Schema.createArray(_elementSchema);
38+
if (_type.isArrayType()) {
39+
schema.addProp(AVRO_SCHEMA_PROP_CLASS, _type.toCanonical());
40+
}
41+
return schema;
3342
}
3443

3544
/*
@@ -50,6 +59,11 @@ public void itemsFormat(JsonFormatVisitable visitable, JavaType type)
5059
@Override
5160
public void itemsFormat(JsonFormatTypes type) throws JsonMappingException
5261
{
53-
_elementSchema = AvroSchemaHelper.simpleSchema(type, _type.getContentType());
62+
// Unlike Jackson, Avro treats character arrays as an array of ints with the java.lang.Character class type.
63+
if (_type.hasRawClass(char[].class)) {
64+
_elementSchema = AvroSchemaHelper.typedSchema(Type.INT, _type.getContentType());
65+
} else {
66+
_elementSchema = AvroSchemaHelper.simpleSchema(type, _type.getContentType());
67+
}
5468
}
5569
}

0 commit comments

Comments
 (0)