Skip to content

Commit 46cbe12

Browse files
committed
Add subType schema into this union, unless it is already there.
1 parent 03d344a commit 46cbe12

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

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

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,25 +85,56 @@ public RecordVisitor(SerializerProvider p, JavaType type, VisitorFormatWrapperIm
8585

8686
List<NamedType> subTypes = getProvider().getAnnotationIntrospector().findSubtypes(bean.getClassInfo());
8787
if (subTypes != null && !subTypes.isEmpty()) {
88-
List<Schema> unionSchemas = new ArrayList<>();
89-
// Initialize with this schema
90-
if (_type.isConcrete()) {
91-
unionSchemas.add(_typeSchema);
92-
}
88+
89+
// At this point calculating hashCode for _typeSchema fails with NPE because RecordSchema.fields is NULL
90+
// (see org.apache.avro.Schema.RecordSchema#computeHash).
91+
// Therefore, _typeSchema must be added into union at very end, or unionSchemas must not be HashSet (or any
92+
// other type calling hashCode() for equality check).
93+
Set<Schema> unionSchemas = new HashSet<>();
94+
// ArrayList<Schema> unionSchemas = new ArrayList<>();
95+
96+
// IdentityHashMap is used because it is using reference-equality.
97+
// Set<Schema> unionSchemas = Collections.newSetFromMap(new IdentityHashMap<>());
98+
99+
// Initialize with this schema is
100+
// if (_type.isConcrete()) {
101+
// unionSchemas.add(_typeSchema);
102+
// }
103+
93104
try {
94105
for (NamedType subType : subTypes) {
95106
JsonSerializer<?> ser = getProvider().findValueSerializer(subType.getType());
96107
VisitorFormatWrapperImpl visitor = _visitorWrapper.createChildWrapper();
97108
ser.acceptJsonFormatVisitor(visitor, getProvider().getTypeFactory().constructType(subType.getType()));
98109
Schema subTypeSchema = visitor.getAvroSchema();
99-
// If subType schema is also UNION, include all its types into this union
110+
// Add subType schema into this union, unless it is already there.
111+
// When subType schema is itself a union, include all its types into this union
100112
if (subTypeSchema.getType() == Type.UNION) {
113+
// subTypeSchema.getTypes().stream()
114+
// .filter(unionElement -> !unionSchemas.contains(unionElement))
115+
// .forEach(unionSchemas::add);
116+
// or
117+
// for( Schema unionElement: subTypeSchema.getTypes()) {
118+
// if (unionSchemas.contains(unionElement)) {
119+
// continue;
120+
// }
121+
// unionSchemas.add(unionElement);
122+
// }
101123
unionSchemas.addAll(subTypeSchema.getTypes());
102124
} else {
125+
// if (!unionSchemas.contains(subTypeSchema)) {
126+
// unionSchemas.add(subTypeSchema);
127+
// }
103128
unionSchemas.add(subTypeSchema);
104129
}
105130
}
106-
_avroSchema = Schema.createUnion(unionSchemas);
131+
132+
ArrayList<Schema> unionList = new ArrayList<>(unionSchemas);
133+
// add _type schema into union
134+
if (_type.isConcrete()) {
135+
unionList.add(_typeSchema);
136+
}
137+
_avroSchema = Schema.createUnion(unionList);
107138
} catch (JsonMappingException jme) {
108139
throw new RuntimeException("Failed to build schema", jme);
109140
}

0 commit comments

Comments
 (0)