c# - 反序列化关系模型的 Newtonsoft.Json 问题
问题描述
我有一个具有父子关系的简单关系模型,如下所示:
public class Parent
{
public Parent(int id)
{
Id = id;
}
public int Id { get; private set; }
public IList<Child> Children { get; set; } = new List<Child>();
}
public class Child
{
public Parent Parent { get; set; }
}
我创建了一个小对象图,其中包含共享同一父级的两个子级的列表:
var parent = new Parent(1);
var child1 = new Child {Parent = parent};
var child2 = new Child {Parent = parent};
parent.Children.Add(child1);
parent.Children.Add(child2);
var data = new List<Child> {child1, child2};
接下来,我使用以下方法对其进行序列化SerializeObject
:
var settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All
};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(data, Formatting.Indented, settings);
据我所见,生成的 json 看起来不错:
{
"$id": "1",
"$values": [
{
"$id": "2",
"Parent": {
"$id": "3",
"Id": 1,
"Children": {
"$id": "4",
"$values": [
{
"$ref": "2"
},
{
"$id": "5",
"Parent": {
"$ref": "3"
}
}
]
}
}
},
{
"$ref": "5"
}
]
}
但是,当我反序列化 json 时,我没有得到预期的对象,因为对于第二个孩子,Parent 属性为空,导致第二个断言失败:
var data2 = Newtonsoft.Json.JsonConvert.DeserializeObject<IList<Child>>(json, settings);
Debug.Assert(data2[0].Parent != null);
Debug.Assert(data2[1].Parent != null);
如果没有构造函数,Parent
则不会出现此问题并且第二个孩子的 Parent 属性具有预期值。
任何想法这可能是什么?
解决方案
参数化构造函数不能很好地工作,PreserveReferencesHandling
因为存在固有的先有鸡还是先有蛋的问题:在反序列化程序需要创建对象时,可能无法从 JSON 加载传递给构造函数的必要信息。所以需要有一种方法让它创建一个空对象,然后稍后填写适当的信息。
这是一个已知的限制,并记录在案:
注意:
当通过非默认构造函数设置值时,无法保留引用。对于非默认构造函数,必须在父值之前创建子值,以便将它们传递给构造函数,从而无法跟踪引用。ISerializable
types 是一个类的示例,其值使用非默认构造函数填充,并且不适用于PreserveReferencesHandling
.
要解决您的模型的问题,您可以在您的Parent
类中创建一个私有的、无参数的构造函数并将其标记为[JsonConstructor]
. 然后用 标记Id
属性[JsonProperty]
,这将允许 Json.Net 使用私有设置器。所以你会有:
public class Parent
{
public Parent(int id)
{
Id = id;
}
[JsonConstructor]
private Parent()
{ }
[JsonProperty]
public int Id { get; private set; }
public IList<Child> Children { get; set; } = new List<Child>();
}
顺便说一句,由于您的所有列表都没有在对象之间共享,因此您可以PreserveReferencesHandling.Objects
使用PreserveReferencesHandling.All
. 这将使 JSON 更小,但仍会保留您关心的引用。
在这里工作演示:https ://dotnetfiddle.net/cLk9DM
推荐阅读
- python - Python Discord Bot 如何在一条消息中发送多行?
- php - 如何将数据库中的每个单元格存储在数组中?
- python - 我无法让我的 python 代码与 crontab 一起使用
- alpine - 在 Alpine 中安装较旧的软件包版本
- php - 同时多个php请求,第二个请求直到第一个完成才完成
- ros - 使用 colcon 在 Robomaker ROS 工作区中安装 python 模块
- r - 如果满足条件,则用向量填充列
- c# - 使用 IgnoreQueryFilters() 的 Entity Framework Linq 查询在延迟加载属性上不起作用,即使包含
- java - spring数据JPA中的排序表达式
- javascript - 无法在 Vue js 变量中赋值