【发布时间】:2020-03-29 11:47:03
【问题描述】:
我希望能够控制 JSON .NET 如何生成其元引用 ID,例如 "$id": "1"。取以下代码:
public class Person
{
public string Name { get; set; }
public Person Mother { get; set; }
}
.
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
Newtonsoft.Json.JsonConvert.DefaultSettings = () => settings;
var person = new Person
{
Name = "bob",
Mother = new Person { Name = "jane" }
};
var personJson = JsonConvert.SerializeObject(person);
var motherJson = JsonConvert.SerializeObject(person.Mother);
person 的 JSON 如下所示:
{
"$id": "1",
"Name": "bob",
"Mother": {
"$id": "2",
"Name": "jane",
"Mother": null
}
}
但是,如果我直接序列化person.Mother,JSON 看起来像这样:
{
"$id": "1",
"Name": "jane",
"Mother": null
}
在第一个 JSON 中,Jane 是 "$id": "2",但直接序列化 Jane 是 "$id": "1"。这是我在正常情况下所期望的行为,因为序列化程序按照遍历对象的顺序分配 ID,但我真的很想覆盖 ID 生成,以便我可以将其设为对象引用本身的哈希。这样一来,无论是作为父级成员序列化还是单独序列化,Jane 每次都会为每个运行的程序实例生成相同的 ID。
更新
对于所选答案中的示例代码和评论中的建议,我使用了IReferenceResolver。事实证明我不能使用它,但无论如何我都会包含下面的代码。这不起作用的原因是因为我试图将 JSON.NET 混为一个快速而肮脏的克隆工具,所以我不能因为它不适合我的需要而责备它。从那以后,我开始使用自己的自定义克隆实用程序,因此不再需要它。
public class ObjectReferenceResolver : Newtonsoft.Json.Serialization.IReferenceResolver
{
readonly Dictionary<object, int> objectDic = new Dictionary<object, int>();
int maxId = 0;
//Called during serialization
public string GetReference(object context, object value)
{
//This method will return the meta $id that you choose. In this example, I am storing
//object references in a dictionary with an incremented ID. If the reference exists, I
//return its ID. Otherwise, I increment the ID and add the reference to the dictionary.
var id = 0;
if (objectDic.ContainsKey(value))
{
id = objectDic[value];
}
else
{
objectDic[value] = maxId++;
}
return id.ToString();
}
//Called during serialization
public bool IsReferenced(object context, object value)
{
//Return whether or not value exists in a reference bank.
//If false, the JSON will return as a full JSON object with "$id": "x"
//If true, the JSON will return "$ref": "x"
return objectDic.ContainsKey(value);
}
//Called during deserialization
public void AddReference(object context, string reference, object value)
{
//This method is called after the deserializer has created a new instance of the
//object. At this time, it's only the initial instance and no properties have been set.
//This method presents a problem because it does not allow you to create the instance or
//retrieve it from a repo and then return it for later use by the reference resolver.
//Therefore, I have to find the existing object by $id, remove it, and then add the new
//object created by the deseralizer. This creates the possibility for two instances of
//the same data object to exist within the running application, so, unfortunately, this
//will not work.
var e = objectDic.First(x => x.Value.ToString() == reference).Key;
objectDic.Remove(e);
objectDic[value] = reference.ParseInt().Value;
}
//Called during deserialization
public object ResolveReference(object context, string reference)
{
//This method retrieves an existing reference by $id and returns it.
var value = objectDic.FirstOrDefault(x => x.Value.ToString() == reference).Key;
return value;
}
}
【问题讨论】:
-
@BrianRogers 请注意,我说的是“对象引用本身”。没有两个对象可以共享同一个引用。
-
自定义
IReferenceResolver可能会满足您的需求。请参阅:How to use custom reference resolving with JSON.NET。 -
你看How to use custom reference resolving with JSON.NET了吗?如果是这样,您的问题是 1)如何为您的对象生成唯一哈希? 2)如何将唯一哈希挂钩到
IReferenceResolver? -
很奇怪,我没有收到您之前评论的通知。不过,我看了看,这个问题似乎是在询问如何将 JSON 中的
categoryID映射到导航属性的id属性,这与我所问的完全不同。我指的是$id属性,它只存在于反序列化器用来保留引用的 JSON 中。我想覆盖序列化程序返回的值,以便它显示类似"$id": "whateveriwant"而不是"$id": "1"。 -
accepted answer 实际上展示了如何使用 Guid 作为
$id和$ref的值。
标签: c# .net serialization reference json.net