C# Json.Net中的PreserveReferencesHandling和ReferenceLoopHandling有什么区别?

C# Json.Net中的PreserveReferencesHandling和ReferenceLoopHandling有什么区别?,c#,asp.net,json,asp.net-web-api,json.net,C#,Asp.net,Json,Asp.net Web Api,Json.net,我正在查看一个WebAPI应用程序示例,其中包含以下代码: json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects; json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; 还有一个是这样编码的:

我正在查看一个WebAPI应用程序示例,其中包含以下代码:

json.SerializerSettings.PreserveReferencesHandling 
   = Newtonsoft.Json.PreserveReferencesHandling.Objects;
json.SerializerSettings.ReferenceLoopHandling 
   = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
还有一个是这样编码的:

json.SerializerSettings.PreserveReferencesHandling 
   = Newtonsoft.Json.PreserveReferencesHandling.Objects;
json.SerializerSettings.ReferenceLoopHandling 
   = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

也不能解释为什么每一个都被选中。我对WebAPI非常陌生,所以有人可以简单地向我解释一下区别是什么,以及为什么我可能需要使用一个而不是另一个

这些设置最好通过示例来解释。假设我们想要代表公司中的员工等级。我们制作了一个简单的类,如下所示:

class Employee
{
    public string Name { get; set; }
    public List<Employee> Subordinates { get; set; }
}
JsonSerializerSettings settings = new JsonSerializerSettings
{
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(employees, settings);
…我们得到以下输出:

[
  {
    "Name": "Angela Anderson",
    "Subordinates": [
      {
        "Name": "Bob Brown",
        "Subordinates": null
      },
      {
        "Name": "Charles Cooper",
        "Subordinates": null
      }
    ]
  },
  {
    "Name": "Bob Brown",
    "Subordinates": null
  },
  {
    "Name": "Charles Cooper",
    "Subordinates": null
  }
]
到目前为止还不错。但是,您会注意到,在JSON中重复了Bob和Charles的信息,因为代表他们的对象被主要员工列表和Angela的下属列表引用。也许现在还可以

现在假设我们还想有一种方法来跟踪每个员工的主管,以及他或她的下属。因此,我们将
员工
模型更改为添加
主管
属性

class Employee
{
    public string Name { get; set; }
    public Employee Supervisor { get; set; }
    public List<Employee> Subordinates { get; set; }
}
有了这个设置,我们得到了以下JSON:

[
  {
    "Name": "Angela Anderson",
    "Supervisor": null,
    "Subordinates": [
      {
        "Name": "Bob Brown",
        "Subordinates": null
      },
      {
        "Name": "Charles Cooper",
        "Subordinates": null
      }
    ]
  },
  {
    "Name": "Bob Brown",
    "Supervisor": {
      "Name": "Angela Anderson",
      "Supervisor": null,
      "Subordinates": [
        {
          "Name": "Charles Cooper",
          "Subordinates": null
        }
      ]
    },
    "Subordinates": null
  },
  {
    "Name": "Charles Cooper",
    "Supervisor": {
      "Name": "Angela Anderson",
      "Supervisor": null,
      "Subordinates": [
        {
          "Name": "Bob Brown",
          "Subordinates": null
        }
      ]
    },
    "Subordinates": null
  }
]
[
  {
    "$id": "1",
    "Name": "Angela Anderson",
    "Supervisor": null,
    "Subordinates": [
      {
        "$id": "2",
        "Name": "Bob Brown",
        "Supervisor": {
          "$ref": "1"
        },
        "Subordinates": null
      },
      {
        "$id": "3",
        "Name": "Charles Cooper",
        "Supervisor": {
          "$ref": "1"
        },
        "Subordinates": null
      }
    ]
  },
  {
    "$ref": "2"
  },
  {
    "$ref": "3"
  }
]
如果检查JSON,应该很清楚此设置的作用:每当序列化程序遇到对已在序列化过程中的对象的引用时,它都会跳过该成员。(这可以防止序列化程序进入无限循环。)您可以看到,在JSON顶部的Angela下属列表中,Bob和Charles都没有显示主管。在JSON的底部,Bob和Charles都将Angela显示为他们的主管,但请注意,此时她的下属列表不包括Bob和Charles

虽然可以使用这个JSON,甚至可以通过一些工作从它重建原始的对象层次结构,但它显然不是最优的。我们可以使用
preserverencesshandling
设置来消除JSON中的重复信息,同时仍然保留对象引用:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    PreserveReferencesHandling = PreserveReferencesHandling.Objects,
    Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(employees, settings);
现在我们得到以下JSON:

[
  {
    "Name": "Angela Anderson",
    "Supervisor": null,
    "Subordinates": [
      {
        "Name": "Bob Brown",
        "Subordinates": null
      },
      {
        "Name": "Charles Cooper",
        "Subordinates": null
      }
    ]
  },
  {
    "Name": "Bob Brown",
    "Supervisor": {
      "Name": "Angela Anderson",
      "Supervisor": null,
      "Subordinates": [
        {
          "Name": "Charles Cooper",
          "Subordinates": null
        }
      ]
    },
    "Subordinates": null
  },
  {
    "Name": "Charles Cooper",
    "Supervisor": {
      "Name": "Angela Anderson",
      "Supervisor": null,
      "Subordinates": [
        {
          "Name": "Bob Brown",
          "Subordinates": null
        }
      ]
    },
    "Subordinates": null
  }
]
[
  {
    "$id": "1",
    "Name": "Angela Anderson",
    "Supervisor": null,
    "Subordinates": [
      {
        "$id": "2",
        "Name": "Bob Brown",
        "Supervisor": {
          "$ref": "1"
        },
        "Subordinates": null
      },
      {
        "$id": "3",
        "Name": "Charles Cooper",
        "Supervisor": {
          "$ref": "1"
        },
        "Subordinates": null
      }
    ]
  },
  {
    "$ref": "2"
  },
  {
    "$ref": "3"
  }
]
请注意,现在在JSON中为每个对象分配了一个连续的
$id
值。对象第一次出现时,将对其进行完整序列化,而随后的引用将替换为特殊的
$ref
属性,该属性使用相应的
$id
引用原始对象。有了这个设置,JSON更加简洁,可以反序列化回原始对象层次结构,而无需额外的工作,假设您使用的库理解JSON.Net/Web API生成的
$id
$ref
符号


那么,你为什么要选择一种或另一种设置呢?当然,这取决于你的需要。如果JSON将被不理解
$id
/
$ref
格式的客户端使用,并且它可以容忍某些地方存在不完整的数据,那么您可以选择使用
ReferenceLoopHandling.Ignore
。如果您正在寻找更紧凑的JSON,并且将使用JSON.Net或Web API(或其他兼容的库)来反序列化数据,那么您将选择使用
PreserveReferencesHandling.Objects
。如果您的数据是无重复引用的有向无环图,则不需要任何设置。

文档中怎么说?有什么不清楚的?回答得很好。当使用最后一个方法(
preserverencesshandling.Objects
)时,它是一个非常棒的库,用于在客户端重新组装对象引用。谢谢,实际上我只是需要它,因为在列表中使用它时,
preserverencesshandling.Objects
的结果不是很好,不确定JsonNetDecycle是否会在浏览器中产生大量开销