Skip to content

Commit a87fb1d

Browse files
k0kamartincostelloschnerring
authored
Support [JsonPolymorphic] and [JsonDerivedType] (#3170)
* Support of JsonPolymorphicAttribute Fix integration Basic and NSwagClientExample tests to have different outputs for .net6 * Apply suggestions from code review Co-authored-by: Martin Costello <[email protected]> * add `Produces` to NSwagClientExmaple to strip unnecessary fields fix `SwaggerEndpoint_ReturnsValidSwaggerJson_DotNet6` test - it wasn't using parameters * Add README from https://github.com/domaindrivendev/Swashbuckle.AspNetCore/pull/2671/files#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5 * Apply suggestions from code review --------- Co-authored-by: martincostello <[email protected]> Co-authored-by: schnerring <[email protected]>
1 parent 85b4808 commit a87fb1d

14 files changed

+2341
-3291
lines changed

README.md

+35-4
Original file line numberDiff line numberDiff line change
@@ -1166,7 +1166,8 @@ services.AddSwaggerGen(c =>
11661166
});
11671167
```
11681168

1169-
_NOTE: If you're using the [Swashbuckle Annotations library](#swashbuckleaspnetcoreannotations), it contains a custom selector that's based on the presence of `SwaggerSubType` attributes on base class definitions. This way, you can use simple attributes to explicitly list the inheritance and/or polymorphism relationships you want to expose. To enable this behavior, check out the [Annotations docs](#list-known-subtypes-for-inheritance-and-polymorphism)._
1169+
> [!NOTE]
1170+
> If you're using the [Swashbuckle Annotations library](#swashbuckleaspnetcoreannotations), it contains a custom selector that's based on the presence of `[JsonDerivedType]` (or `[SwaggerSubType]` for .NET 6 or earlier) attributes on base class definitions. This way, you can use simple attributes to explicitly list the inheritance and/or polymorphism relationships you want to expose. To enable this behavior, check out the [Annotations docs](#list-known-subtypes-for-inheritance-and-polymorphism).
11701171

11711172
#### Describing Discriminators ####
11721173

@@ -1232,7 +1233,8 @@ services.AddSwaggerGen(c =>
12321233
});
12331234
```
12341235

1235-
_NOTE: If you're using the [Swashbuckle Annotations library](#swashbuckleaspnetcoreannotations), it contains custom selector functions that are based on the presence of `SwaggerDiscriminator` and `SwaggerSubType` attributes on base class definitions. This way, you can use simple attributes to explicitly provide discriminator metadata. To enable this behavior, check out the [Annotations docs](#enrich-polymorphic-base-classes-with-discriminator-metadata)._
1236+
> [!NOTE]
1237+
> If you're using the [Swashbuckle Annotations library](#swashbuckleaspnetcoreannotations), it contains custom selector functions that are based on the presence of `[JsonPolymorphic]` (or `[SwaggerDiscriminator]` for .NET 6 or earlier) and `[JsonDerivedType]` (or `[SwaggerSubType]` for .NET 6 or earlier) attributes on base class definitions. This way, you can use simple attributes to explicitly provide discriminator metadata. To enable this behavior, check out the [Annotations docs](#enrich-polymorphic-base-classes-with-discriminator-metadata).
12361238

12371239
## Swashbuckle.AspNetCore.SwaggerUI ##
12381240

@@ -1540,6 +1542,15 @@ services.AddSwaggerGen(c =>
15401542
});
15411543

15421544
// Shape.cs
1545+
1546+
// .NET 7 or later
1547+
[JsonDerivedType(typeof(Rectangle))]
1548+
[JsonDerivedType(typeof(Circle))]
1549+
public abstract class Shape
1550+
{
1551+
}
1552+
1553+
// .NET 6 or earlier
15431554
[SwaggerSubType(typeof(Rectangle))]
15441555
[SwaggerSubType(typeof(Circle))]
15451556
public abstract class Shape
@@ -1549,7 +1560,7 @@ public abstract class Shape
15491560

15501561
### Enrich Polymorphic Base Classes with Discriminator Metadata ###
15511562

1552-
If you're using annotations to _explicitly_ indicate the "known" subtypes for a polymorphic base type, you can combine the `SwaggerDiscriminatorAttribute` with the `SwaggerSubTypeAttribute` to provide additional metadata about the "discriminator" property, which will then be incorporated into the generated schema definition:
1563+
If you're using annotations to _explicitly_ indicate the "known" subtypes for a polymorphic base type, you can combine the `JsonPolymorphicAttribute` with the `JsonDerivedTypeAttribute` to provide additional metadata about the "discriminator" property, which will then be incorporated into the generated schema definition:
15531564

15541565

15551566
```csharp
@@ -1560,12 +1571,32 @@ services.AddSwaggerGen(c =>
15601571
});
15611572

15621573
// Shape.cs
1574+
1575+
// .NET 7 or later
1576+
[JsonPolymorphic(TypeDiscriminatorPropertyName = "shapeType")]
1577+
[JsonDerivedType(typeof(Rectangle), "rectangle")]
1578+
[JsonDerivedType(typeof(Circle), "circle")]
1579+
public abstract class Shape
1580+
{
1581+
// Avoid using a JsonPolymorphicAttribute.TypeDiscriminatorPropertyName
1582+
// that conflicts with a property in your type hierarchy.
1583+
// Related issue: https://github.com/dotnet/runtime/issues/72170
1584+
}
1585+
1586+
// .NET 6 or earlier
15631587
[SwaggerDiscriminator("shapeType")]
15641588
[SwaggerSubType(typeof(Rectangle), DiscriminatorValue = "rectangle")]
15651589
[SwaggerSubType(typeof(Circle), DiscriminatorValue = "circle")]
15661590
public abstract class Shape
15671591
{
1568-
public ShapeType { get; set; }
1592+
public ShapeType ShapeType { get; set; }
1593+
}
1594+
1595+
[JsonConverter(typeof(JsonStringEnumConverter))]
1596+
public enum ShapeType
1597+
{
1598+
Circle,
1599+
Rectangle
15691600
}
15701601
```
15711602

src/Swashbuckle.AspNetCore.Annotations/AnnotationsSwaggerGenOptionsExtensions.cs

+34
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Text.Json.Serialization;
45
using Swashbuckle.AspNetCore.SwaggerGen;
56
using Swashbuckle.AspNetCore.Annotations;
67

@@ -75,6 +76,17 @@ private static IEnumerable<Type> AnnotationsSubTypesSelector(Type type)
7576
return obsoleteAttribute.SubTypes;
7677
}
7778

79+
#if NET7_0_OR_GREATER
80+
var jsonDerivedTypeAttributes = type.GetCustomAttributes(false)
81+
.OfType<JsonDerivedTypeAttribute>()
82+
.ToList();
83+
84+
if (jsonDerivedTypeAttributes.Count > 0)
85+
{
86+
return jsonDerivedTypeAttributes.Select(attr => attr.DerivedType);
87+
}
88+
#endif
89+
7890
return Enumerable.Empty<Type>();
7991
}
8092

@@ -100,6 +112,17 @@ private static string AnnotationsDiscriminatorNameSelector(Type baseType)
100112
return obsoleteAttribute.Discriminator;
101113
}
102114

115+
#if NET7_0_OR_GREATER
116+
var jsonPolymorphicAttributes = baseType.GetCustomAttributes(false)
117+
.OfType<JsonPolymorphicAttribute>()
118+
.FirstOrDefault();
119+
120+
if (jsonPolymorphicAttributes != null)
121+
{
122+
return jsonPolymorphicAttributes.TypeDiscriminatorPropertyName;
123+
}
124+
#endif
125+
103126
return null;
104127
}
105128

@@ -117,6 +140,17 @@ private static string AnnotationsDiscriminatorValueSelector(Type subType)
117140
return subTypeAttribute.DiscriminatorValue;
118141
}
119142

143+
#if NET7_0_OR_GREATER
144+
var jsonDerivedTypeAttributes = baseType.GetCustomAttributes(false)
145+
.OfType<JsonDerivedTypeAttribute>()
146+
.FirstOrDefault(attr => attr.DerivedType == subType);
147+
148+
if (jsonDerivedTypeAttributes is { TypeDiscriminator: string discriminator })
149+
{
150+
return discriminator;
151+
}
152+
#endif
153+
120154
baseType = baseType.BaseType;
121155
}
122156

0 commit comments

Comments
 (0)