Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for JsonPolymorphic and JsonDerivedType attributes to Swashbuckle.AspNetCore.Annotations for .NET7 and later #2671

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,7 @@ services.AddSwaggerGen(c =>
});
```

_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)._
_NOTE: 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)._

#### Describing Discriminators ####

Expand Down Expand Up @@ -1200,7 +1200,7 @@ services.AddSwaggerGen(c =>
});
```

_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)._
_NOTE: 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)._

## Swashbuckle.AspNetCore.SwaggerUI ##

Expand Down Expand Up @@ -1483,6 +1483,15 @@ services.AddSwaggerGen(c =>
});

// Shape.cs

// .NET 7 or later
[JsonDerivedType(typeof(Rectangle))]
[JsonDerivedType(typeof(Circle))]
public abstract class Shape
{
}

// .NET 6 or earlier
[SwaggerSubType(typeof(Rectangle))]
[SwaggerSubType(typeof(Circle))]
public abstract class Shape
Expand All @@ -1492,7 +1501,7 @@ public abstract class Shape

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

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:
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:


```csharp
Expand All @@ -1503,12 +1512,32 @@ services.AddSwaggerGen(c =>
});

// Shape.cs

// .NET 7 or later
[JsonPolymorphic(TypeDiscriminatorPropertyName = "shapeType")]
[JsonDerivedType(typeof(Rectangle), "rectangle")]
[JsonDerivedType(typeof(Circle), "circle")]
public abstract class Shape
{
// Avoid using a JsonPolymorphicAttribute.TypeDiscriminatorPropertyName
// that conflicts with a property in your type hierarchy.
// Related issue: https://github.com/dotnet/runtime/issues/72170
}

// .NET 6 or earlier
[SwaggerDiscriminator("shapeType")]
[SwaggerSubType(typeof(Rectangle), DiscriminatorValue = "rectangle")]
[SwaggerSubType(typeof(Circle), DiscriminatorValue = "circle")]
public abstract class Shape
{
public ShapeType { get; set; }
public ShapeType ShapeType { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AnimalType
{
Circle,
Rectangle
}
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.Annotations;

Expand Down Expand Up @@ -56,8 +57,20 @@ public static void EnableAnnotations(this SwaggerGenOptions options)

private static IEnumerable<Type> AnnotationsSubTypesSelector(Type type)
{
#if NET7_0_OR_GREATER
var jsonDerivedTypeAttributes = type.GetCustomAttributes(false)
.OfType<JsonDerivedTypeAttribute>()
.ToArray();

if (jsonDerivedTypeAttributes.Any())
{
return jsonDerivedTypeAttributes.Select(attr => attr.DerivedType);
}
#endif

var subTypeAttributes = type.GetCustomAttributes(false)
.OfType<SwaggerSubTypeAttribute>();
.OfType<SwaggerSubTypeAttribute>()
.ToArray();

if (subTypeAttributes.Any())
{
Expand All @@ -78,6 +91,17 @@ private static IEnumerable<Type> AnnotationsSubTypesSelector(Type type)

private static string AnnotationsDiscriminatorNameSelector(Type baseType)
{
#if NET7_0_OR_GREATER
var jsonPolymorphicAttribute = baseType.GetCustomAttributes(false)
.OfType<JsonPolymorphicAttribute>()
.FirstOrDefault();

if (jsonPolymorphicAttribute is not null)
{
return jsonPolymorphicAttribute.TypeDiscriminatorPropertyName;
}
#endif

var discriminatorAttribute = baseType.GetCustomAttributes(false)
.OfType<SwaggerDiscriminatorAttribute>()
.FirstOrDefault();
Expand All @@ -104,6 +128,17 @@ private static string AnnotationsDiscriminatorValueSelector(Type subType)
if (subType.BaseType == null)
return null;

#if NET7_0_OR_GREATER
var jsonDerivedTypeAttribute = subType.BaseType.GetCustomAttributes(false)
.OfType<JsonDerivedTypeAttribute>()
.FirstOrDefault(attr => attr.DerivedType == subType);

if (jsonDerivedTypeAttribute is not null)
{
return jsonDerivedTypeAttribute.TypeDiscriminator?.ToString();
}
#endif

var subTypeAttribute = subType.BaseType.GetCustomAttributes(false)
.OfType<SwaggerSubTypeAttribute>()
.FirstOrDefault(attr => attr.SubType == subType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Swashbuckle.AspNetCore.Annotations
{
#if NET7_0_OR_GREATER
[Obsolete("Use JsonPolymorphic attribute instead")]
#endif
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)]
public class SwaggerDiscriminatorAttribute : Attribute
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

namespace Swashbuckle.AspNetCore.Annotations
{
#if NET7_0_OR_GREATER
[Obsolete("Use JsonDerivedType attribute instead")]
#endif
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = true)]
public class SwaggerSubTypeAttribute : Attribute
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ public void CreateAnimal([Required]Animal animal)
}
}

[SwaggerDiscriminator("animalType")]
[SwaggerSubType(typeof(Cat), DiscriminatorValue = "Cat")]
[SwaggerSubType(typeof(Dog), DiscriminatorValue = "Dog")]

[JsonPolymorphic(TypeDiscriminatorPropertyName = "animalType")]
[JsonDerivedType(typeof(Cat), "Cat")]
[JsonDerivedType(typeof(Dog), "Dog")]
public class Animal
{
public AnimalType AnimalType { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion test/WebSites/NswagClientExample/NswagClientExample.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<DotNetSwaggerPath>$([System.IO.Path]::Combine("..", "..", "..", "src", "Swashbuckle.AspNetCore.Cli", "bin", $(Configuration), $(TargetFramework), "dotnet-swagger.dll"))</DotNetSwaggerPath>
</PropertyGroup>

Expand Down