我有以下情况。我将问题简化为以下示例,尽管我的真实情况更为复杂。
System.Text.Json不会完全序列化对象,但Newtonsoft Json.NET可以。
假设我有以下类结构。
public class A
{
public string AProperty { get; set; } = "A";
}
public class A<T> : A where T : class, new()
{
public T TObject { get; set; } = new T();
}
public class B
{
public string BProperty { get; set; } = "B";
}
public class B<T> : B where T : class, new()
{
public T TObject { get; set; } = new T();
}
public class C
{
public string CProperty { get; set; } = "C";
}
下面是一个简单的.NET Core程序:
public class Program
{
private static void Main(string[] args)
{
var obj = new A<B> { TObject = new B<C>() };
var systemTextSerialized = JsonSerializer.Serialize(obj);
var newtonsoftSerialized = JsonConvert.SerializeObject(obj);
}
}
序列化结果如下:
System.Text.Json
{
"TObject": {
"BProperty": "B"
},
"AProperty": "A"
}
牛顿软件
{
"TObject": {
"TObject": {
"CProperty": "C"
},
"BProperty": "B"
},
"AProperty": "A"
}
由于我的应用程序的结构,我不知道B
的通用参数。我只知道这是一个A<B>
.B
的实际TObject
直到运行时才知道。
为什么这两种序列化方法不同?有没有办法让 System.Text.Json 完全序列化对象,还是我需要编写一个自定义转换器?
这是记录在案的System.Text.Json
限制。 从文档中:
序列化派生类的属性
在 .NET 7 之前的版本中,System.Text.Json不支持多态类型层次结构的序列化。例如,如果将属性定义为接口或抽象类,则仅序列化在接口或抽象类上定义的属性,即使运行时类型具有其他属性也是如此。本节介绍了此行为的例外情况。
若要序列化 [a] 派生类型的属性,请使用下列方法之一:
- 将
调用序列化的重载,允许您在运行时指定类型...
要序列化的对象声明为
object
。
在您的情况下,A<B>.TObject
被声明为类型B
,但在您构造的实例中实际上是类型B<C>
,因此根据文档,只有基类B
的属性被序列化。 所以就是这样。 有关进一步的讨论,请参阅已关闭的问题System.Text.Json.JsonSerializer 不序列化派生类 #31742 的属性。
但是,有几种解决方法可用。 首先,您可以将obj
构造为其最有可能派生的类型A<B<C>>
:
var obj = new A<B<C>> { TObject = new B<C>() };
现在,TObject
的所有属性都已序列化。 演示小提琴#1在这里。 但不幸的是,您不能使用此解决方法,因为B
的实际TObject
直到运行时才知道。
或者,如果您只需要序列化您的obj
,您可以按照文档中的建议 #2 声明一个object
类型的代理项属性,并序列化它:
public class A<T> : A where T : class, new()
{
[System.Text.Json.Serialization.JsonPropertyName("TObject")]
[Newtonsoft.Json.JsonIgnore]
public object SerializedTObject => TObject;
[System.Text.Json.Serialization.JsonIgnore]
public T TObject { get; set; } = new T();
}
请注意,不得为要序列化的只读属性设置JsonSerializerOptions.IgnoreReadOnlyProperties
。
演示小提琴#2在这里。
最后,如果需要多态序列化和反序列化,则需要编写自定义JsonConverter
或(在 .NET 7 及更高版本中(使用适当的属性批注标记基类型。 要开始使用,请参阅
- 在System.Text.Json中可以进行多态反序列化吗?
- 使用 .NET Core System.Text.Json 序列化/反序列化类层次结构
- System.Text.Json 和动态解析多态对象。