Skip to content

Commit 8a0df0a

Browse files
committed
Fix #161
1 parent 4ac24ab commit 8a0df0a

File tree

6 files changed

+269
-5
lines changed

6 files changed

+269
-5
lines changed

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public UnionReader newReader(AvroReadContext parent, AvroParserImpl parser) {
3333
@Override
3434
public JsonToken nextToken() throws IOException
3535
{
36-
int index = _decodeIndex();
36+
int index = _parser.decodeIndex();
3737
// important: remember to create new instance
3838
// also: must pass our parent (not this instance)
3939
AvroStructureReader reader = _memberReaders[index].newReader(_parent, _parser);
@@ -42,7 +42,7 @@ public JsonToken nextToken() throws IOException
4242

4343
@Override
4444
public void skipValue(AvroParserImpl parser) throws IOException {
45-
int index = _decodeIndex();
45+
int index = _decodeIndex(parser.decodeIndex());
4646
// NOTE: no need to create new instance since it's stateless call and
4747
// we pass decoder to use
4848
_memberReaders[index].skipValue(parser);
@@ -53,14 +53,13 @@ public String nextFieldName() throws IOException {
5353
nextToken();
5454
return null;
5555
}
56-
56+
5757
@Override
5858
protected void appendDesc(StringBuilder sb) {
5959
sb.append('?');
6060
}
6161

62-
private final int _decodeIndex() throws IOException {
63-
int index = _parser.decodeIndex();
62+
private final int _decodeIndex(int index) throws IOException {
6463
if (index < 0 || index >= _memberReaders.length) {
6564
throw new JsonParseException(_parser, String.format
6665
("Invalid index (%s); union only has %d types", index, _memberReaders.length));
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.fasterxml.jackson.dataformat.avro.schemaev;
2+
3+
import com.fasterxml.jackson.dataformat.avro.AvroMapper;
4+
import com.fasterxml.jackson.dataformat.avro.AvroSchema;
5+
import com.fasterxml.jackson.dataformat.avro.AvroTestBase;
6+
7+
import java.util.List;
8+
9+
public class UnionArrayEvolutionTest extends AvroTestBase {
10+
11+
static String SCHEMA_V1_ARRAY_JSON = aposToQuotes("{\n"+
12+
" 'namespace':'org.example.testsnippets',\n"+
13+
" 'type':'record',\n"+
14+
" 'name':'TestDto',\n"+
15+
" 'fields':[\n"+
16+
" {\n"+
17+
" 'name':'id',\n"+
18+
" 'type':['string', 'null']\n"+
19+
" }\n"+
20+
" ]\n"+
21+
"}\n");
22+
23+
static String SCHEMA_V2_ARRAY_JSON = aposToQuotes("{\n"+
24+
" 'namespace':'org.example.testsnippets',\n"+
25+
" 'type':'record',\n"+
26+
" 'name':'TestDto',\n"+
27+
" 'fields':[\n"+
28+
" {\n"+
29+
" 'name':'id',\n"+
30+
" 'type':['string', 'null']\n"+
31+
" },\n"+
32+
" {\n"+
33+
" 'name':'names',\n"+
34+
" 'type':['null', { 'type' :'array', 'items' : 'string'}]\n"+
35+
" }\n"+
36+
" ]\n"+
37+
"}\n");
38+
39+
private final AvroMapper MAPPER = getMapper();
40+
41+
static class V1{
42+
public String id;
43+
44+
public V1(){}
45+
46+
public V1(String id) {
47+
this.id = id;
48+
}
49+
}
50+
51+
static class V2 {
52+
public String id;
53+
public List<String> names;
54+
55+
public V2(String id, List<String> names) {
56+
this.id = id;
57+
this.names = names;
58+
}
59+
60+
}
61+
62+
/*
63+
/**********************************************************
64+
/* Test methods
65+
/**********************************************************
66+
*/
67+
68+
public void testRoundtripToOlderCompatibleSchema() throws Exception
69+
{
70+
final AvroSchema srcSchema = MAPPER.schemaFrom(SCHEMA_V2_ARRAY_JSON);
71+
final AvroSchema dstSchema = MAPPER.schemaFrom(SCHEMA_V1_ARRAY_JSON);
72+
final AvroSchema xlate = srcSchema.withReaderSchema(dstSchema);
73+
74+
byte[] avro = MAPPER.writer(srcSchema).writeValueAsBytes(new V2("test", null));
75+
V1 result = MAPPER.readerFor(V1.class)
76+
.with(xlate)
77+
.readValue(avro);
78+
assertEquals("test", result.id);
79+
}
80+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.fasterxml.jackson.dataformat.avro.schemaev;
2+
3+
import com.fasterxml.jackson.dataformat.avro.AvroMapper;
4+
import com.fasterxml.jackson.dataformat.avro.AvroSchema;
5+
import com.fasterxml.jackson.dataformat.avro.AvroTestBase;
6+
7+
import java.util.Map;
8+
9+
public class UnionMapEvolutionTest extends AvroTestBase {
10+
11+
static String SCHEMA_V1_ARRAY_JSON = aposToQuotes("{\n"+
12+
" 'namespace':'org.example.testsnippets',\n"+
13+
" 'type':'record',\n"+
14+
" 'name':'TestDto',\n"+
15+
" 'fields':[\n"+
16+
" {\n"+
17+
" 'name':'id',\n"+
18+
" 'type':['string', 'null']\n"+
19+
" }\n"+
20+
" ]\n"+
21+
"}\n");
22+
23+
static String SCHEMA_V2_ARRAY_JSON = aposToQuotes("{\n"+
24+
" 'namespace':'org.example.testsnippets',\n"+
25+
" 'type':'record',\n"+
26+
" 'name':'TestDto',\n"+
27+
" 'fields':[\n"+
28+
" {\n"+
29+
" 'name':'id',\n"+
30+
" 'type':['string', 'null']\n"+
31+
" },\n"+
32+
" {\n"+
33+
" 'name':'names',\n"+
34+
" 'type':['null', { 'type' :'map', 'values' : ['string']}]\n"+
35+
" }\n"+
36+
" ]\n"+
37+
"}\n");
38+
39+
private final AvroMapper MAPPER = getMapper();
40+
41+
static class V1{
42+
public String id;
43+
44+
public V1(){}
45+
46+
public V1(String id) {
47+
this.id = id;
48+
}
49+
}
50+
51+
static class V2 {
52+
public String id;
53+
public Map<String,String> names;
54+
55+
public V2(String id, Map<String,String> names) {
56+
this.id = id;
57+
this.names = names;
58+
}
59+
60+
}
61+
62+
/*
63+
/**********************************************************
64+
/* Test methods
65+
/**********************************************************
66+
*/
67+
68+
public void testRoundtripToOlderCompatibleSchema() throws Exception
69+
{
70+
final AvroSchema srcSchema = MAPPER.schemaFrom(SCHEMA_V2_ARRAY_JSON);
71+
final AvroSchema dstSchema = MAPPER.schemaFrom(SCHEMA_V1_ARRAY_JSON);
72+
final AvroSchema xlate = srcSchema.withReaderSchema(dstSchema);
73+
74+
byte[] avro = MAPPER.writer(srcSchema).writeValueAsBytes(new V2("test", null));
75+
V1 result = MAPPER.readerFor(V1.class)
76+
.with(xlate)
77+
.readValue(avro);
78+
assertEquals("test", result.id);
79+
}
80+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.fasterxml.jackson.dataformat.avro.schemaev;
2+
3+
import com.fasterxml.jackson.dataformat.avro.AvroMapper;
4+
import com.fasterxml.jackson.dataformat.avro.AvroSchema;
5+
import com.fasterxml.jackson.dataformat.avro.AvroTestBase;
6+
7+
public class UnionRecordEvolutionTest extends AvroTestBase {
8+
9+
static String SCHEMA_V1_ARRAY_JSON = aposToQuotes("{\n" +
10+
" 'namespace':'org.example.testsnippets',\n" +
11+
" 'type':'record',\n" +
12+
" 'name':'TestDto',\n" +
13+
" 'fields':[\n" +
14+
" {\n" +
15+
" 'name':'id',\n" +
16+
" 'type':['string', 'null']\n" +
17+
" }\n" +
18+
" ]\n" +
19+
"}\n");
20+
21+
static String SCHEMA_V2_ARRAY_JSON = aposToQuotes("{\n" +
22+
" 'namespace':'org.example.testsnippets',\n" +
23+
" 'type':'record',\n" +
24+
" 'name':'TestDto',\n" +
25+
" 'fields':[\n" +
26+
" {\n" +
27+
" 'name':'id',\n" +
28+
" 'type':['string', 'null']\n" +
29+
" },\n" +
30+
" {\n" +
31+
" 'name':'names',\n" +
32+
" 'type':['null', \n" +
33+
" { " +
34+
" 'type' : 'record', \n" +
35+
" 'name' : 'testRecord', \n" +
36+
" 'fields' : [\n" +
37+
" {\n" +
38+
" 'name': 'firstName',\n" +
39+
" 'type': 'string'\n" +
40+
" }] \n" +
41+
" } \n" +
42+
" ]\n" +
43+
" }\n" +
44+
" ]\n" +
45+
"}\n");
46+
47+
private final AvroMapper MAPPER = getMapper();
48+
49+
static class V1 {
50+
public String id;
51+
52+
public V1() {
53+
}
54+
55+
public V1(String id) {
56+
this.id = id;
57+
}
58+
}
59+
60+
static class V2 {
61+
public String id;
62+
public Names names;
63+
64+
public V2(String id, Names names) {
65+
this.id = id;
66+
this.names = names;
67+
}
68+
69+
private class Names {
70+
public String firstName;
71+
72+
public Names() {
73+
}
74+
75+
public Names(String firstName) {
76+
this.firstName = firstName;
77+
}
78+
}
79+
}
80+
81+
/*
82+
/**********************************************************
83+
/* Test methods
84+
/**********************************************************
85+
*/
86+
87+
public void testRoundtripToOlderCompatibleSchema() throws Exception {
88+
final AvroSchema srcSchema = MAPPER.schemaFrom(SCHEMA_V2_ARRAY_JSON);
89+
final AvroSchema dstSchema = MAPPER.schemaFrom(SCHEMA_V1_ARRAY_JSON);
90+
final AvroSchema xlate = srcSchema.withReaderSchema(dstSchema);
91+
92+
byte[] avro = MAPPER.writer(srcSchema).writeValueAsBytes(new V2("test", null));
93+
V1 result = MAPPER.readerFor(V1.class)
94+
.with(xlate)
95+
.readValue(avro);
96+
assertEquals("test", result.id);
97+
}
98+
}

release-notes/CREDITS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,8 @@ Guido Medina (guidomedina@github)
8181
Alexander Cyon (Sajjon@github)
8282
* Reported #159: (cbor) Some short UTF Strings encoded using non-canonical form
8383
(2.9.9)
84+
85+
Łukasz Dziedziak (lukidzi@github)
86+
* Reported, contributed fix for #161: (avro) Deserialize from newer version to older
87+
one throws NullPointerException
88+
(2.9.9)

release-notes/VERSION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Project: jackson-datatypes-binaryModules:
1212

1313
#159: (cbor) Some short UTF Strings encoded using non-canonical form
1414
(reported by Alexander C)
15+
#161: (avro) Deserialize from newer version to older one throws NullPointerException
16+
(reported, fix contributed by Łukasz D)
1517

1618
2.9.8 (15-Dec-2018)
1719

0 commit comments

Comments
 (0)