繁体   English   中英

JSON .NET7 中的多态序列化 Web API

[英]JSON Polymorphic serialization in .NET7 Web API

.NET7 对System.Text.Json序列化程序进行了大量改进,其中之一是使用新的[JsonPolymorphic]属性对类型进行多态序列化。 我试图在我的 Asp.Net web API 中使用它,但它似乎没有序列化类型鉴别器,尽管 model 已正确设置。

它仅在尝试通过网络发送对象时发生,在使用 JsonSerializer 时,一切似乎都运行良好。 例如:

// This is my data model
[JsonPolymorphic]
[JsonDerivedType(typeof(SuccesfulResult), typeDiscriminator: "ok")]
[JsonDerivedType(typeof(ErrorResult), typeDiscriminator: "fail")]
public abstract record Result;
public record SuccesfulResult : Result;
public record ErrorResult(string Error) : Result;
// Some test code that actually works
var testData = new Result[]
{
    new SuccesfulResult(),
    new ErrorResult("Whoops...")
};

var serialized = JsonSerializer.SerializeToDocument(testData);
// Serialized string looks like this:
// [{ "$type": "ok" }, { "$type": "fail", "error": "Whoops..." }]
// So type discriminators are in place and all is well
var deserialized = serialized.Deserialize<IEnumerable<Result>>()!;

// This assertion passes succesfully!
// We deserialized a collection polymorphically and didn't lose any type information.
testData.ShouldDeepEqual(deserialized);
// However, when sending the object as a response in an API, the AspNet serializer
// seems to be ignoring the attributes:

[HttpGet("ok")]
public Result GetSuccesfulResult() => new SuccesfulResult();

[HttpGet("fail")]
public Result GetFailResult() => new ErrorResult("Whoops...");

这些响应都没有使用类型鉴别器进行注释,而且我的强类型客户端无法将结果反序列化为适当的层次结构。

GET /api/ok HTTP/1.1
# =>
# ACTUAL: {}
# EXPECTED: { "$type": "ok" }
GET /api/fail HTTP/1.1
# =>
# ACTUAL: { "error": "Whoops..." }
# EXPECTED: { "$type": "fail", "error": "Whoops..." }

我是否缺少某种 API 配置,它会使控制器以多态方式序列化结果?

在各个子类基类型上指定[JsonDerivedType(...)]似乎可以解决问题,但似乎不是故意的。 这可能会在未来的版本中得到修复。

您必须在序列化时指定基类型才能工作,而无需使用[JsonDerivedType(...)]注释每个派生类型

var serialized = JsonSerializer.SerializeToDocument<Result[]>(testData);

更新:

这是在此处此处跟踪的 ASP.NET Core 中的一个已知错误。 (不影响 IEnumerable 结果)

该链接有一个 .NET 团队成员在此评论中使用自定义解析器的解决方法,如下所示



    var options = new JsonSerializerOptions { TypeInfoResolver = new InheritedPolymorphismResolver() };
    JsonSerializer.Serialize(new Derived(), options); // {"$type":"derived","Y":0,"X":0}
    
    [JsonDerivedType(typeof(Base), typeDiscriminator: "base")]
    [JsonDerivedType(typeof(Derived), typeDiscriminator: "derived")]
    public class Base
    {
        public int X { get; set; }
    }
    
    public class Derived : Base
    {
        public int Y { get; set; }
    }
    
    public class InheritedPolymorphismResolver : DefaultJsonTypeInfoResolver
    {
        public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options)
        {
            JsonTypeInfo typeInfo = base.GetTypeInfo(type, options);
    
            // Only handles class hierarchies -- interface hierarchies left out intentionally here
            if (!type.IsSealed && typeInfo.PolymorphismOptions is null && type.BaseType != null)
            {
                // recursively resolve metadata for the base type and extract any derived type declarations that overlap with the current type
                JsonPolymorphismOptions? basePolymorphismOptions = GetTypeInfo(type.BaseType, options).PolymorphismOptions;
                if (basePolymorphismOptions != null)
                {
                    foreach (JsonDerivedType derivedType in basePolymorphismOptions.DerivedTypes)
                    {
                        if (type.IsAssignableFrom(derivedType.DerivedType))
                        {
                            typeInfo.PolymorphismOptions ??= new()
                            {
                                IgnoreUnrecognizedTypeDiscriminators = basePolymorphismOptions.IgnoreUnrecognizedTypeDiscriminators,
                                TypeDiscriminatorPropertyName = basePolymorphismOptions.TypeDiscriminatorPropertyName,
                                UnknownDerivedTypeHandling = basePolymorphismOptions.UnknownDerivedTypeHandling,
                            };
    
                            typeInfo.PolymorphismOptions.DerivedTypes.Add(derivedType);
                        }
                    }
                }
            }
    
            return typeInfo;
        }
    }

应该固定在.NET 8

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM