作者: SIDDHARTH PANDEY,原文地址
我最近发表了一篇文章,解释了如何使用C#和Newtonsoft.JSON处理JSON。使用Newtonsoft.Json框架是序列化和反序列化任何.NET对象的最佳方法。在本文中,我将告诉您在使用Newtonsoft.Json框架时如何解决自引用循环问题。
假设在尝试使用以某种方式引用自身的JsonConvert.serialize object()序列化.NET对象时,在运行应用程序时,Newtonsoft.Json.JsonSerializationException类型的未处理异常将在Newtonsoft.Json.dll中遇到。您还将找到一些附加信息,例如“附加信息:检测到类型为“some class name.Path”的自引用循环。
让我们用一个例子来理解这个问题的原因。我将使用我在上一篇文章中最初使用的Author类。类定义如下:
public class Author { public string Name { get; set; } public List<string> Courses { get; set; } public DateTimeOffset Since { get; set; } public bool IsHappy { get; set; } public string Country { get; set; } public int Age { get; set; } public List<Author> FavouriteAuthors { get; set; } }
为了快速查看结果,我使用了一个控制台应用程序,其程序类如下所示:
class Program { static void Main(string[] args) { Console.Clear(); ProcessByValueReferenceDemo.ShowDemo(); Console.ReadLine(); } }
下面让我们来看看ProcessByValueReferenceDemo类的ShowDemo方法的实现:
public class ProcessByValueReferenceDemo { public static void ShowDemo() { var sid = new Author() { Name = "Sid", Country = "India", IsHappy = true }; var sidhu = new Author() { Name = "Sidhu", Country = "India", IsHappy = true }; var siddharth = new Author() { Name = "Siddharth", Country = "India", IsHappy = true }; sid.FavouriteAuthors = new List<Author>() { sid, sidhu, sid, siddharth, sidhu, siddharth }; try { JsonConvert.SerializeObject(sid); // An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll // Additional information: Self referencing loop detected with type 'JSONPlayground.Author'.Path 'FavouriteAuthors'. } catch (JsonSerializationException e) { // Console.WriteLine(e); } string authorData = JsonConvert.SerializeObject(sid, new JsonSerializerSettings() { PreserveReferencesHandling = PreserveReferencesHandling.Objects, Formatting = Formatting.Indented }); Console.WriteLine(Environment.NewLine + authorData); } }
现在让我们理解上面的代码:
- 第5-7行定义了3个不同的Author对象。
- 然后,第9行定义名为sid的对象的favoriteAuthors属性。请注意,已将同一对象添加到此属性中。
- 在第13行执行JsonConvert.SerializeObject(sid)时,会引发异常,该异常表示“Newtonsoft.Json.JsonSerializationException类型的未处理异常发生在Newtonsoft.Json.dll中”。这背后的主要原因是JsonConvert在默认情况下按值而不是引用处理对象。您可以在代码中看到,我已将此语句添加到try-catch块中,以便应用程序不会因此异常而停止。
- 第22-26行使用SerializeObject的重载并传递JsonSerializerSettings对象,该对象指示底层JsonSerializer通过使用preservereferencesshandling=preservereferencesshandling.Objects通过引用处理此对象。通过指定此设置,对象现在已正确序列化。此外,格式将缩进以格式化结果。结果如下:
{
"$id": "1",
"Name": "Sid",
"Courses": null,
"Since": "0001-01-01T00:00:00+00:00",
"IsHappy": true,
"Country": "India",
"Age": 0,
"FavouriteAuthors": [
{
"$ref": "1"
},
{
"$id": "2",
"Name": "Sidhu",
"Courses": null,
"Since": "0001-01-01T00:00:00+00:00",
"IsHappy": true,
"Country": "India",
"Age": 0,
"FavouriteAuthors": null
},
{
"$ref": "1"
},
{
"$id": "3",
"Name": "Siddharth",
"Courses": null,
"Since": "0001-01-01T00:00:00+00:00",
"IsHappy": true,
"Country": "India",
"Age": 0,
"FavouriteAuthors": null
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]
}
让我们看看仅仅通过使用PreserveReferencesHandling.Objects对结果输出的魔力。您将注意到,每个JSON对象现在在第2、14和27行都有一个名为$id的新成员。然后这个标识符被用在需要引用的地方,例如在第11、24、37、40行。
这基本上只创建一个对象并在需要时引用它。另外,这样做的好处是引用保留在JSON结果中,当您将此JSON反序列化回Author类型的.NET对象时,它将再次仅创建一次源对象,并将创建另一个仅引用源对象的类似对象。
PreserveReferencesHandling是一个枚举,它允许在对象、数组、所有(数组和对象)上应用相同的概念。默认值为“无”。
我希望这有助于解决使用Newtonsoft.Json时的自引用循环问题。我很快会写更多关于这个流行的高性能JSON框架的文章。如果你想得到更新,请订阅我的博客。