Skip to content

Reuse of schema in derived classes (anyof) creates empty schema in open api spec with polymorphic json type info resolver #64024

@guid0h

Description

@guid0h

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

ReusedModel schema is empty:

{
  "openapi": "3.1.1",
  "info": {
    "title": "OpenApiRepro | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://localhost:7170/"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "OpenApiRepro"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Parent"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Parent": {
        "required": [
          "$type"
        ],
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/ParentChildA"
          },
          {
            "$ref": "#/components/schemas/ParentChildB"
          }
        ],
        "discriminator": {
          "propertyName": "$type",
          "mapping": {
            "ChildA": "#/components/schemas/ParentChildA",
            "ChildB": "#/components/schemas/ParentChildB"
          }
        }
      },
      "ParentChildA": {
        "required": [
          "models"
        ],
        "properties": {
          "$type": {
            "enum": [
              "ChildA"
            ],
            "type": "string"
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReusedModel"
            }
          }
        }
      },
      "ParentChildB": {
        "required": [
          "models"
        ],
        "properties": {
          "$type": {
            "enum": [
              "ChildB"
            ],
            "type": "string"
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReusedModel"
            }
          }
        }
      },
      "ReusedModel": { }
    }
  },
  "tags": [
    {
      "name": "OpenApiRepro"
    }
  ]
}

Expected Behavior

ReusedModel schema is described correctly:

{
  "openapi": "3.1.1",
  "info": {
    "title": "OpenApiRepro | v1",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://localhost:7170/"
    }
  ],
  "paths": {
    "/": {
      "get": {
        "tags": [
          "OpenApiRepro"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Parent"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Parent": {
        "required": [
          "$type"
        ],
        "type": "object",
        "anyOf": [
          {
            "$ref": "#/components/schemas/ParentChildA"
          },
          {
            "$ref": "#/components/schemas/ParentChildB"
          }
        ],
        "discriminator": {
          "propertyName": "$type",
          "mapping": {
            "ChildA": "#/components/schemas/ParentChildA",
            "ChildB": "#/components/schemas/ParentChildB"
          }
        }
      },
      "ParentChildA": {
        "required": [
          "models"
        ],
        "properties": {
          "$type": {
            "enum": [
              "ChildA"
            ],
            "type": "string"
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReusedModel"
            }
          }
        }
      },
      "ParentChildB": {
        "required": [
          "models"
        ],
        "properties": {
          "$type": {
            "enum": [
              "ChildB"
            ],
            "type": "string"
          },
          "models": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReusedModel"
            }
          }
        }
      },
      "ReusedModel": {
        "required": [
          "myStringProperty"
        ],
        "type": "object",
        "properties": {
          "myStringProperty": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "OpenApiRepro"
    }
  ]
}

Steps To Reproduce

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<JsonOptions>(options => options.SerializerOptions.TypeInfoResolver = new PolymorphicJsonTypeResolver());

builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();

app.MapGet(
    "/",
    () =>
    {
        return TypedResults.Ok();
    }
).Produces<Parent>();

app.Run();

public record ReusedModel(string? MyStringProperty);

public record ChildA(IEnumerable<ReusedModel> Models) : Parent;

public record ChildB(IEnumerable<ReusedModel> Models) : Parent;

public abstract record Parent;

public class PolymorphicJsonTypeResolver : DefaultJsonTypeInfoResolver
{

    public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
    {
        JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
        if (jsonTypeInfo.Type == typeof(Parent))
        {
            jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
            {
                DerivedTypes =
                {
                    new JsonDerivedType(typeof(ChildA), "ChildA"),
                    new JsonDerivedType(typeof(ChildB), "ChildB"),
                },
            };
        }

        return jsonTypeInfo;
    }
}

Exceptions (if any)

No response

.NET Version

10.0.100-rc.1.25451.107

Anything else?

Microsoft.AspNetCore.OpenApi 10.0.0-rc.1.25451.107

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions