Skip to content

Commit df63446

Browse files
committed
Merge branch '2.x' into 3.x
2 parents ab07a2b + 1d33114 commit df63446

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
run: ./mvnw -B -q -ff -ntp test
5656
- name: Publish code coverage
5757
if: ${{ matrix.release_build && github.event_name != 'pull_request' }}
58-
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
58+
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
5959
with:
6060
token: ${{ secrets.CODECOV_TOKEN }}
6161
files: ./target/site/jacoco/jacoco.xml

avro/src/main/java/tools/jackson/dataformat/avro/schema/RecordVisitor.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,8 @@ public RecordVisitor(SerializationContext ctxt, JavaType type,
116116
// (see org.apache.avro.Schema.RecordSchema#computeHash).
117117
// Therefore, unionSchemas must not be HashSet (or any other type
118118
// using hashCode() for equality check).
119-
// Set ensures that each subType schema is once in resulting union.
120-
// IdentityHashMap is used because it is using reference-equality.
121-
final Set<Schema> unionSchemas = Collections.newSetFromMap(new IdentityHashMap<>());
119+
// ArrayList ensures that ordering of subTypes is preserved.
120+
final List<Schema> unionSchemas = new ArrayList<>();
122121
// Initialize with this schema
123122
if (_type.isConcrete()) {
124123
unionSchemas.add(_typeSchema);
@@ -140,14 +139,27 @@ public RecordVisitor(SerializationContext ctxt, JavaType type,
140139
unionSchemas.add(subTypeSchema);
141140
}
142141
}
143-
_avroSchema = Schema.createUnion(new ArrayList<>(unionSchemas));
142+
_avroSchema = Schema.createUnion(deduplicateByReference(unionSchemas));
144143
} else {
145144
_avroSchema = _typeSchema;
146145
}
147146
}
148147
_visitorWrapper.getSchemas().addSchema(type, _avroSchema);
149148
}
150149

150+
private static List<Schema> deduplicateByReference(List<Schema> schemas) {
151+
final List<Schema> result = new ArrayList<>();
152+
// Set based on IdentityHashMap is used because we need to deduplicate by reference.
153+
final Set<Schema> seenSchemas = Collections.newSetFromMap(new IdentityHashMap<>());
154+
155+
for(Schema s : schemas) {
156+
if(seenSchemas.add(s)) {
157+
result.add(s); // preserve order
158+
}
159+
}
160+
return result;
161+
}
162+
151163
@Override
152164
public Schema builtAvroSchema() {
153165
if (!_overridden) {

avro/src/test/java/tools/jackson/dataformat/avro/schema/PolymorphicTypeAnnotationsTest.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,9 +254,17 @@ public void base_class_explicitly_in_Union_annotation_test() throws Exception {
254254
}
255255

256256
@Union({
257-
// Interface being explicitly in @Union led to StackOverflowError exception.
258-
DocumentInterface.class,
259-
Word.class, Excel.class})
257+
// Interface being explicitly in @Union led to StackOverflowError exception.
258+
DocumentInterface.class,
259+
// We added a bunch of implementations to test deterministic ordering of the schemas' subtypes ordering.
260+
Word.class,
261+
Excel.class,
262+
Pdf.class,
263+
PowerPoint.class,
264+
TextDocument.class,
265+
Markdown.class,
266+
HtmlDocument.class
267+
})
260268
interface DocumentInterface {
261269
}
262270

@@ -266,11 +274,32 @@ static class Word implements DocumentInterface {
266274
static class Excel implements DocumentInterface {
267275
}
268276

277+
static class Pdf implements DocumentInterface {
278+
}
279+
280+
static class PowerPoint implements DocumentInterface {
281+
}
282+
283+
static class TextDocument implements DocumentInterface {
284+
}
285+
286+
static class Markdown implements DocumentInterface {
287+
}
288+
289+
static class HtmlDocument implements DocumentInterface {
290+
}
291+
292+
269293
@Test
270294
public void interface_explicitly_in_Union_annotation_test() throws Exception {
271295
// GIVEN
272296
final Schema wordSchema = MAPPER.schemaFor(Word.class).getAvroSchema();
273297
final Schema excelSchema = MAPPER.schemaFor(Excel.class).getAvroSchema();
298+
final Schema pdfSchema = MAPPER.schemaFor(Pdf.class).getAvroSchema();
299+
final Schema powerPointSchema = MAPPER.schemaFor(PowerPoint.class).getAvroSchema();
300+
final Schema textSchema = MAPPER.schemaFor(TextDocument.class).getAvroSchema();
301+
final Schema markdownSchema = MAPPER.schemaFor(Markdown.class).getAvroSchema();
302+
final Schema htmlSchema = MAPPER.schemaFor(HtmlDocument.class).getAvroSchema();
274303

275304
// WHEN
276305
Schema actualSchema = MAPPER.schemaFor(DocumentInterface.class).getAvroSchema();
@@ -279,6 +308,16 @@ public void interface_explicitly_in_Union_annotation_test() throws Exception {
279308

280309
// THEN
281310
assertThat(actualSchema.getType()).isEqualTo(Schema.Type.UNION);
282-
assertThat(actualSchema.getTypes()).containsExactlyInAnyOrder(wordSchema, excelSchema);
311+
312+
// Deterministic order: exactly as declared in @Union (excluding the interface).
313+
assertThat(actualSchema.getTypes()).containsExactly(
314+
wordSchema,
315+
excelSchema,
316+
pdfSchema,
317+
powerPointSchema,
318+
textSchema,
319+
markdownSchema,
320+
htmlSchema
321+
);
283322
}
284323
}

0 commit comments

Comments
 (0)