Repository navigation
Cannot use a custom sort for OpenApiDocument.Tags in v2 #2678
Description
Activity
Hi Paul Spangler (@spanglerco)
Thank you for using the SDK and for reaching out.I think that should be easily fixable with the following change in the setter
+_tags = value is HashSet<OpenApiTag> tags && tags.Comparer != EqualityComparer<OpenApiTag>.Default ? -_tags = value is HashSet<OpenApiTag> tags && tags.Comparer is OpenApiTagComparer ?
And corresponding unit test.
Is this something you'd like to submit a pull request for provided some guidance?
Start your work from support/v2 and target support/v2 so we can easily propagate the changes to v3 as well.
Let me know if you have any additional comments or questions.
- addedtype:bugA broken experienceA broken experiencepriority:p2Medium. Generally has a work-around and a smaller sub-set of customers is affected. SLA <=30 daysMedium. Generally has a work-around and a smaller sub-set of customers is affected. SLA <=30 daystype:regressionA bug from previous releaseA bug from previous release
on Jan 15, 2026 spanglerco commented
on Jan 15, 2026 ContributorAuthorMore actionsHi Vincent Biret (@baywet), I think that would still require the passed in set be a
HashSet<T>. Perhaps it could be an OR:+_tags = value is not HashSet<OpenApiTags> hashSet || hashSet.Comparer != EqualityComparer<OpenApiTag>.Default ? + value : -_tags = value is HashSet<OpenApiTag> tags && tags.Comparer is OpenApiTagComparer ? - tags :
I'll see if I have any time to submit something.
Thank you for the additional information.
I think the main order of that change when I made it was to have documents that are compliant with the specification when duplicated tags are being passed to the object model.
Each tag name in the list MUST be unique.
Exiting on a different type will lead to most other ISet implementations NOT to have the deduplication implementation, which is most likely a regression we don't want. And with the collection expressions, it's likely that most people "won't know" which implementation they are using.
The only ISet implementations that maintain order are SortedSet and ImmutableSortedSet (unless somebody is rolling out a custom implementation, but that's probably a very limited number of people...). In that case it might be preferable to assumed that people passing a sorted set like you, know what they are doing, and took care of the duplication themselves.
So maybe something like that would be better in the end
+_tags = value is HashSet<OpenApiTag> tags && tags.Comparer != EqualityComparer<OpenApiTag>.Default || value is SortedSet<OpenApiTag> || value is ImmutableSortedSet<OpenApiTag> ? -_tags = value is HashSet<OpenApiTag> tags && tags.Comparer is OpenApiTagComparer ?
thoughts?
spanglerco commented
on Jan 15, 2026 ContributorAuthorMore actionsVincent Biret (@baywet) Yeah, I think a whitelist is reasonable. For what it's worth,
ISet<T>can't be targeted by a collection expression (currently), so callers have to choose something.Thank you for the additional information.
I was under the impression that the dotnet team was working on "default implementations for collections expressions targeting abstract and interface types mappings" for net 10. This would have been awesome and saved some more "ceremony" when initializing things. But this probably got bumped to net 11 I guess.
- linked a pull request that will close this issuefix: Support custom tag ordering #2679
on Jan 16, 2026
Describe the bug
In the v1 API,
OpenApiDocument.Tagswas anIList<OpenApiTag>, enabling us to order the tags however we want (e.g., in a Swashbuckle document filter). In the v2 API, the property is anISet<OpenApiTag>, which, while more difficult, would let us use a custom ordering viaSortedSet<OpenApiTag>with a custom comparer. However, theOpenApiDocument.Tagssetter doesn't actually preserve the collection you give it, unless it's specifically aHashSet<OpenApiTag>that usesOpenApiTagComparer.Technically, fully populating a
SortedSet<OpenApiTag>before assigning it toOpenApiDocument.Tagsoften "works", because the currentHashSetimplementation will enumerate in the same order as insertion in many cases (I think it's if there aren't any hash code collisions and you don't remove any elements and add more). But initializingOpenApiDocument.Tagsto aSortedSet<OpenApiTag>and then populating it doesn't produce the desired result.OpenApi File To Reproduce
Any document with multiple tags. For example:
{ "openapi": "3.0.4", "tags": [ { "name": "Data", "description": "..." }, { "name": "Export", "description": "..." }, { "name": "Tables", "description": "..." }, { "name": "Versioning", "description": "..." } ] }Expected behavior
I can order the document's tags such that the serialized document maintains the order I want instead of alphabetic:
{ "openapi": "3.0.4", "tags": [ { "name": "Versioning", "description": "..." }, { "name": "Tables", "description": "..." }, { "name": "Data", "description": "..." }, { "name": "Export", "description": "..." } ] }Screenshots/Code Snippets
Swashbuckle document filter that works with the v1 API:
Code that "works" in v2, assuming the
HashSetcooperates:Additional context
Perhaps the property setter should only override the
ISetimplementation if given aHashSetthat uses the default comparer and/or an empty set?