[译]如何解决使用Newtonsoft.Json时的自引用循环问题?

作者: 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框架的文章。如果你想得到更新,请订阅我的博客。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注

16 − 1 =