[英]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")]
必须在BaseType
和DerivedType
上重复,如下所示:
// 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]
属性复制到所有相关的基类型来手动执行此操作,或者编写一些自定义契约解析器来自动将派生类型传播到基类型。
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).显然,实现远非完美,需要在功能和性能方面进行大量改进(支持来自属性的鉴别器,可能缓存类型信息,甚至可能使用源生成器)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.