Skip to content

Commit

Permalink
Support [JsonPolymorphic] and [JsonDerivedType] (#3170)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
3 people authored Nov 25, 2024
1 parent 85b4808 commit a87fb1d
Show file tree
Hide file tree
Showing 14 changed files with 2,341 additions and 3,291 deletions.
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

0 comments on commit a87fb1d

Please sign in to comment.