Conversation
ModelResolver.resolveSchemaMembers() merged the examples parsed from @Schema(examples = {...}) into any examples already present on the schema: if (schema.getExamples() == null || schema.getExamples().isEmpty()) { schema.setExamples(parsedExamples); } else { schema.getExamples().addAll(parsedExamples); } The resolver can apply the same annotation to the same schema more than once (e.g. when a downstream tool such as springdoc-openapi re-processes an already resolved schema). The addAll branch then appended the same values again, producing duplicated entries such as ["Hello", "World", "Hello", "World"]. Assign instead of merge so the resolution is idempotent. This is safe because AnnotationsUtils.mergeSchemaAnnotations() resolves the type-level and member-level examples() by picking one side (it never concatenates them), so there is no distinct source to merge. Adds ModelResolverExamplesTest covering both the direct double-application and the re-resolve path; both fail before the fix (expected [2] but found [4]) and pass after it.
3 tasks
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes duplicated
examplesentries on a schema when the same@Schema(examples = {...})annotation is applied to a schema twice. The examples resolution is now idempotent.Problem
ModelResolver.resolveSchemaMembers(...)merges theexamplesmember of the@Schemaannotation into any examples already present on the target schema:The resolver is not guaranteed to visit a given schema exactly once. When it processes the same annotation for a schema that already carries examples (for example when a downstream tool such as springdoc-openapi re-processes an already-resolved schema during the Jackson 3 migration), the
addAllbranch appends the parsed values again, producing output such as:instead of
This was observed in practice after the Jackson 3 migration, where schema resolution is re-run and the previously merged examples were duplicated.
Change
Replace the conditional merge with a plain assignment so that the annotation is always authoritative for the examples of the schema it is applied to:
parseExamplesArray(...)already reads the current schema state (it consultsschema.type()andschema.nullable()to decide how to parse each value), so no information is lost by assigning rather than appending.Tests
Added
ModelResolverExamplesTest:resolvingSchemaMembersTwiceDoesNotDuplicateExamples— applies the same@Schema(examples)to a schema that already carries examples and asserts the list is unchanged.resolvingSameModelTwiceKeepsExamplesStable— resolves a model once, re-runs the schema member resolution, and asserts the examples list is unchanged.Both tests fail before the fix (
expected [2] but found [4]) and pass after it.Alternatives considered
An alternative fix keeps the merge but de-duplicates:
This was rejected because
AnnotationsUtils.mergeSchemaAnnotations(...)resolves the type-level and member-levelexamples()by picking one side (it never concatenates them), so by the time this code runsschema.getExamples()can only hold the current annotation's own values, from a previous visit. There is no distinct source to merge, so plain assignment is equivalent and simpler. Also verified empirically that the two variants produce identical output for member-only, type-level-only, and member-plus-type-level cases.Notes
This is the upstream port of fork PR vpelikh#20.