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

Support of JsonPolymorphic and JsonDerivedType attributes #3170

Merged
merged 6 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1166,7 +1166,8 @@ 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 @@ -1232,7 +1233,8 @@ 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 @@ -1540,6 +1542,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 @@ -1549,7 +1560,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 @@ -1560,12 +1571,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 ShapeType
{
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 @@ -75,6 +76,17 @@ private static IEnumerable<Type> AnnotationsSubTypesSelector(Type type)
return obsoleteAttribute.SubTypes;
}

#if NET7_0_OR_GREATER
var jsonDerivedTypeAttributes = type.GetCustomAttributes(false)
.OfType<JsonDerivedTypeAttribute>()
.ToList();

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

return Enumerable.Empty<Type>();
}

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

#if NET7_0_OR_GREATER
var jsonPolymorphicAttributes = baseType.GetCustomAttributes(false)
.OfType<JsonPolymorphicAttribute>()
.FirstOrDefault();

if (jsonPolymorphicAttributes != null)
{
return jsonPolymorphicAttributes.TypeDiscriminatorPropertyName;
}
#endif

return null;
}

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

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

if (jsonDerivedTypeAttributes is { TypeDiscriminator: string discriminator })
{
return discriminator;
}
#endif

baseType = baseType.BaseType;
}

Expand Down
Loading