简体   繁体   English

如何在 .NET 7 中使用 System.Text.Json 序列化多级多态类型层次结构?

[英]How can I serialize a multi-level polymorphic type hierarchy with System.Text.Json in .NET 7?

I have a multi-level polymorphic type hierarchy that I previously serialized using the data contract serializers.我有一个多级多态类型层次结构,我之前使用数据协定序列化程序对其进行了序列化。 I would like to convert that to System.Text.Json using the new type hierarchy support in .NET 7. Where should I apply the [JsonDerivedType] attributes so that "grandchild" and other deeply derived subtypes of subtypes can be serialized correctly?我想使用 .NET 7 中的新类型层次结构支持将其转换为 System.Text.Json。我应该在哪里应用[JsonDerivedType]属性,以便“孙子”和子类型的其他深度派生子类型可以正确序列化?

My original type hierarchy looked like this:我原来的类型层次结构是这样的:

[KnownType(typeof(DerivedType))]
public abstract class BaseType { } // Properties omitted

[KnownType(typeof(DerivedOfDerivedType))]
public class DerivedType : BaseType { public string DerivedValue { get; set; } } 

public class DerivedOfDerivedType : DerivedType { public string DerivedOfDerivedValue { get; set; } }

I replaced the [KnownType] attributes with [JsonDerivedType] attributes as follows:我用[JsonDerivedType]属性替换了[KnownType]属性,如下所示:

[JsonDerivedType(typeof(DerivedType), "DerivedType:#MyNamespace")]
public abstract class BaseType { } // Properties omitted

[JsonDerivedType(typeof(DerivedOfDerivedType), "DerivedOfDerivedType:#MyNamespace")]
public class DerivedType : BaseType { public string DerivedValue { get; set; } } 

public class DerivedOfDerivedType : DerivedType { public string DerivedOfDerivedValue { get; set; } }

However when I serialize as List<BaseType> as follows:但是,当我按如下方式序列化为List<BaseType>时:

var list = new List<BaseType> { new DerivedOfDerivedType { DerivedValue = "value 1", DerivedOfDerivedValue = "value of DerivedOfDerived" } };
var json = JsonSerializer.Serialize(list);

I get the following exception:我得到以下异常:

System.NotSupportedException: Runtime type 'MyNamespace.DerivedOfDerivedType' is not supported by polymorphic type 'MyNamespace.BaseType'. Path: $.
 ---> System.NotSupportedException: Runtime type 'MyNamespace.DerivedOfDerivedType' is not supported by polymorphic type 'MyNamespace.BaseType'.

Where should the JsonDerivedType attributes be applied to make this work?应该在何处应用JsonDerivedType属性以使其工作?

The [JsonDerivedType] attribute must be applied to every base type (other than System.Object ) that might be declared for serialization . [JsonDerivedType]属性必须应用于可能声明为序列化的每个基本类型( System.Object除外)

Thus [JsonDerivedType(typeof(DerivedOfDerivedType), "DerivedOfDerivedType:#MyNamespace")] must be duplicated on BaseType and DerivedType like so:因此[JsonDerivedType(typeof(DerivedOfDerivedType), "DerivedOfDerivedType:#MyNamespace")]必须在BaseTypeDerivedType上重复,如下所示:

// Derived types of BaseType
[JsonDerivedType(typeof(DerivedType), "DerivedType:#MyNamespace")]
// Derived types of DerivedType copied from DerivedType
[JsonDerivedType(typeof(DerivedOfDerivedType), "DerivedOfDerivedType:#MyNamespace")] 
public abstract class BaseType { } // Properties omitted

[JsonDerivedType(typeof(DerivedOfDerivedType), "DerivedOfDerivedType:#MyNamespace")]
public class DerivedType : BaseType { public string DerivedValue { get; set; } } 

public class DerivedOfDerivedType : DerivedType { public string DerivedOfDerivedValue { get; set; } }

Notes:笔记:

  • The [JsonDerivedType] attribute must be applied to the intermediate type DerivedType in order to serialize values that are declared to be DerivedType , eg: [JsonDerivedType]属性必须应用于中间类型DerivedType以便序列化声明为DerivedType的值,例如:

     var list = new List<DerivedType> { new DerivedOfDerivedType { DerivedValue = "value 1", DerivedOfDerivedValue = "value of DerivedOfDerived" } }; var json = JsonSerializer.Serialize(list);

    If intermediate types in the polymorphic type hierarchy are never serialized independently, [JsonDerivedType] need only be applied only to the root base type.如果多态类型层次结构中的中间类型从不独立序列化,则[JsonDerivedType]只需要应用于根基类型。

  • While the data contract serializers and XmlSerializer will automatically discover known types of known types recursively during serialization of a base type, it seems that this feature was omitted from System.Text.Json.虽然数据协定序列化程序和XmlSerializer会在基本类型的序列化期间自动递归地发现已知类型的已知类型,但似乎 System.Text.Json 中省略了此功能。 Thus the application must do this manually by copying [JsonDerivedType] attributes onto all relevant base types, or alternatively writing some custom contract resolver that propagates the derived types to base types automatically.因此,应用程序必须通过将[JsonDerivedType]属性复制到所有相关的基类型来手动执行此操作,或者编写一些自定义契约解析器来自动将派生类型传播到基类型。

Demo fiddle here .演示小提琴在这里

I've dabbled with the same task and wrote some POC custom contact resolver which applies all JsonDerivedTypeAttribute 's from the hierarchy to the root:我已经涉足相同的任务并编写了一些 POC 自定义联系人解析器,它将所有JsonDerivedTypeAttribute从层次结构应用到根:

static void AddNestedDerivedTypes(JsonTypeInfo jsonTypeInfo)
{
    if (jsonTypeInfo.PolymorphismOptions is null) return;

    var derivedTypes = jsonTypeInfo.PolymorphismOptions.DerivedTypes
        .Where(t => Attribute.IsDefined(t.DerivedType, typeof(JsonDerivedTypeAttribute)))
        .Select(t => t.DerivedType)
        .ToList();
    var hashset = new HashSet<Type>(derivedTypes);
    var queue = new Queue<Type>(derivedTypes);
    while (queue.TryDequeue(out var derived))
    {
        if (!hashset.Contains(derived))
        {
            // Todo: handle discriminators
            jsonTypeInfo.PolymorphismOptions.DerivedTypes.Add(new JsonDerivedType(derived, derived.FullName));
            hashset.Add(derived);
        }

        var attribute = derived.GetCustomAttributes<JsonDerivedTypeAttribute>();
        foreach (var jsonDerivedTypeAttribute in attribute) queue.Enqueue(jsonDerivedTypeAttribute.DerivedType);
    }
}

Which can be set up in the options:可以在选项中设置:

var options = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver
    {
        Modifiers = { AddNestedDerivedTypes }
    }
};
SomeRootType container = ...;
var json = JsonSerializer.Serialize(container, options);
var typedToBase = JsonSerializer.Deserialize<SomeRootType>(json, options);

Obviously implementation is far from perfect and requires a lot of refining both feature- and performance-wise (supporting discriminators from the attributes, possibly caching type infos, maybe even using source generators).显然,实现远非完美,需要在功能和性能方面进行大量改进(支持来自属性的鉴别器,可能缓存类型信息,甚至可能使用源生成器)。

Demo fiddle演示小提琴

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

相关问题 使用 .NET Core System.Text.Json 序列化/反序列化类层次结构 - Serialize/Deserialize a class hierarchy with .NET Core System.Text.Json ASP.NET Core 3.1 如何让 controller 使用 System.Text.Json 进行序列化和反序列化 - ASP.NET Core 3.1 How can I get the controller to use System.Text.Json to serialize and deserialize 如何使用 System.Text.Json 序列化文字 JSON 值? - How can I serialize a literal JSON value with System.Text.Json? 如何使用 System.Text.Json 将 Newtonsoft JToken 序列化为 JSON? - How can I serialize a Newtonsoft JToken to JSON using System.Text.Json? 如何使用 System.Text.Json 将 double[,] 2d 数组序列化为 JSON? - How can I serialize a double[,] 2d array to JSON using System.Text.Json? 如何序列化堆栈<T>使用 System.Text.Json 转换为 JSON 而不颠倒堆栈? - How can I serialize a Stack<T> to JSON using System.Text.Json without reversing the stack? .Net System.Text.Json 无法序列化 object - .Net System.Text.Json cannot serialize object 如何避免使用 System.Text.Json 编写`$type`? - How do I avoid writing `$type` with System.Text.Json? System.Text.Json 中是否可以进行多态反序列化? - Is polymorphic deserialization possible in System.Text.Json? 如何在 System.Text.Json 中序列化抽象 class - How to Serialize abstract class in System.Text.Json
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM