System.Text.Json Swashbuckle 中未显示 .NET 5 中的字段序列化 API 定义

System.Text.Json Field Serialization in .NET 5 not shown in Swashbuckle API Definition

问题

我正在使用 ASP.NET Core 和 .NET 5,并且正在使用 System.Text.Json 序列化程序来序列化包含字段的类型(例如 System.Numerics.Vector3(X、Y 和 Z 是字段) ,尽管任何类型的字段在此处的行为都相同)。

我已经通过在 Postman 上调用 API 来验证字段是否正确序列化,但是 Swashbuckle 生成的 Swagger API 定义没有正确反映这一点。 (定义只是显示一个空类型)

复制

我创建了一个 gist 来重现这个。 它在 /api/Test 处提供了一个 HTTP Get 方法,其中 returns 一个类型为 Test 的对象,带有一个字段和一个 属性。两者都是字符串。 通过 Postman returns 调用此 API 两者的正确值。 查看 /swagger 处的 Swagger UI 或 /swagger/v1/swagger.json 处的定义仅显示 属性.

此行为也适用于 Swagger UI 中的示例,其中仅包含属性。

预期行为

根据文档,Swagger 生成器应该自动复制 System.Text.Json 的行为,它明确配置为序列化字段(参见 line 47),所以我希望 Swagger 定义包括领域。

总结

重申一下,我使用 System.Text.Json 来序列化具有 public 字段的类型。这行得通,我更愿意保持这样。

我尝试使用 Swashbuckle 生成 API 这些序列化的 returns 文档。这仅适用于属性,但不适用于字段。

是否需要明确配置其他内容才能使其正常工作?

问题与Swagger无关,纯序列化问题

您有 3 个解决方案:

  1. 为矢量编写您自己的自定义 json。 (只是概念)
  2. 使用具有原始类型的自定义对象并对其进行映射。 (只是概念)
  3. 使用Newtonsoft.Json(建议的解决方案)

关于微软docSystem.Text.Json你可以在比较列表中看到,System.Text.Json可能有一些限制。

If you want the suggested solution jump directly to solution 3.

让我们来看看自定义的第一个概念serialized。顺便说一句,此自定义示例仅用于演示,并非完整的解决方案。

所以你可以做的是:

  1. 创建自定义向量 CustomVector 模型。
  2. 创建扩展 JsonConverter.
  3. 的自定义 VectorConverter class
  4. 添加了一些映射。
  5. 将属性 VectorConverter 放入向量 属性。

这是我的 CustomVector 尝试:

public class CustomVector
{
    public float? X { get; set; }
    public float? Y { get; set; }
    public float? Z { get; set; }
}

和自定义 VectorConverter:

public class VectorConverter : JsonConverter<Vector3>
{
    public override Vector3 Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // just for now
        return new Vector3();
    }

    public override void Write(Utf8JsonWriter writer, Vector3 data, JsonSerializerOptions options)
    {
        // just for now
        var customVector = new CustomVector
        {
            X = data.X,
            Y = data.Y,
            Z = data.Z
        };

        var result = JsonSerializer.Serialize(customVector);

        writer.WriteStringValue(result);
    }
}

而你的向量属性,添加了以下属性:

[JsonConverter(typeof(VectorConverter))]
public Vector3 Vector { get; set; }

这将 return 以下结果:

现在这解决了部分问题,如果你想post一个矢量对象,你将面临另一个挑战,这也取决于你的实现逻辑。

因此,我的第二次解决方案尝试是公开我们的自定义向量并忽略 json 中的 vector3 并从我们的代码映射它 to/from Vector3:

因此我们引入了一个 CustomVector,我们可以在我们的模型中使用它来代替 Vector3,而不是将它映射到我们的 Vector3。

public class Test
{
    public string Field { get; set; }
    public string Property { get; set; }
    [JsonIgnore]
    public Vector3 Vector { get; set; }
    public CustomVector CustomVector { get; set; }
}

这是一个 get 和 post 映射示例的方法:

[HttpGet]
public Test Get()
{
    var vector = new CustomVector() { X = 1, Y = 1, Z = 1 };
    var test = new Test
    {
        Field = "Field",
        Property = "Property",
        CustomVector = vector
    };
    VectorMapping(test);
    return test;
}

[HttpPost]
public Test Post(Test test)
{
    VectorMapping(test);
    return test;
}

private static void VectorMapping(Test test)
{
    test.Vector = new Vector3
    {
        X = test.CustomVector.X.GetValueOrDefault(),
        Y = test.CustomVector.Y.GetValueOrDefault(),
        Z = test.CustomVector.Z.GetValueOrDefault()
    };
}

第一个解决方案的缺点是,我们需要编写完整的自定义序列化,而在第二个解决方案中,我们引入了额外的模型和映射。

建议的解决方案

因此我建议进行以下和第三次尝试:

保留解决方案中的所有内容,只需将 nuget Swashbuckle.AspNetCore.Newtonsoft 添加到项目中,例如:

<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.6.3" />

在你的创业公司

services.AddSwaggerGenNewtonsoftSupport();

启动,这将生成文档,因为它允许序列化和反序列化 Vector3 和 System.Text.Json 不支持的其他 class 类型。

如您所见,文档中现在包含了 Vector3:

我很确定这可以通过其他方式完成。所以这是我解决它的尝试。

Swashbuckle 似乎没有使用 JsonSerializerOptions 来生成文档。我发现的一种解决方法是手动处理类型:

public class FieldsSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        var fields = context.Type.GetFields();

        if (fields == null) return;
        if (fields.Length == 0) return;

        foreach (var field in fields)
        {
            schema.Properties[field.Name] = new OpenApiSchema
            {
                // this should be mapped to an OpenApiSchema type
                Type = field.FieldType.Name
            };
        }
    }
}

然后在你的 Startup.cs ConfigureServices:

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" });
    c.SchemaFilter<FieldsSchemaFilter>();
});

单步执行时,您会看到 SchemaFilterContext (SchemaGenerator) 中使用了 JsonSerializerOptionsIncludeFields 设置为真。仍然只有属性用于文档,所以我想像这样的过滤器是你最好的选择。