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

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; }
}

这是一家小公司,目前只有三名员工:安吉拉、鲍勃和查尔斯.安吉拉是老板,鲍勃和查尔斯是她的下属.让我们设置数据来描述这种关系:

Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };

angela.Subordinates = new List<Employee> { bob, charles };

List<Employee> employees = new List<Employee> { angela, bob, charles };

如果我们将员工列表序列化为JSON.

string json = JsonConvert.SerializeObject(employees, Formatting.Indented);
Console.WriteLine(json);

...我们得到这个输出:

[
  {
    "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的下属列表引用.也许现在还可以.

现在假设我们还希望有一种方法来跟踪每个员工的主管以及他或她的下属.所以我们改变了我们的Employee型号,增加了Supervisor属性.

class Employee
{
    public string Name { get; set; }
    public Employee Supervisor { get; set; }
    public List<Employee> Subordinates { get; set; }
}

...在我们的设置代码中再添加几行,表示Charles和Bob向Angela报告:

Employee angela = new Employee { Name = "Angela Anderson" };
Employee bob = new Employee { Name = "Bob Brown" };
Employee charles = new Employee { Name = "Charles Cooper" };

angela.Subordinates = new List<Employee> { bob, charles };
bob.Supervisor = angela;       // added this line
charles.Supervisor = angela;   // added this line

List<Employee> employees = new List<Employee> { angela, bob, charles };

但现在我们有点问题.因为对象图中有引用循环(例如,angela个引用bob,而bob个引用angela),所以当我们try 序列化员工列表时,会得到JsonSerializationException.解决这个问题的一种方法是将ReferenceLoopHandling设置为Ignore,如下所示:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
    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
  }
]

如果您判断JSON,应该很清楚这个设置的作用:每当序列化程序遇到一个已经在序列化过程中的对象的引用时,它都会跳过该成员.(这可以防止序列化程序进入无限循环.)你可以看到,在JSON顶部Angela的下属列表中,Bob和Charles都没有显示主管.在JSON的底部,Bob和Charles都显示Angela是他们的主管,但请注意,她的下属列表此时不包括Bob和Charles.

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

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

string json = JsonConvert.SerializeObject(employees, settings);

现在我们得到以下JSON:

[
  {
    "$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就可以反序列化为原始的对象层次 struct ,而不需要额外的工作.

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

Asp.net相关问答推荐

.Net Framework:w3wp.exe 中的异常

'Access-Control-Allow-Origin' 标头包含多个值 '*, *',但只允许一个

是否可以在没有那些 .svn 文件夹的情况下从 subversion 签出文件?

iframe.readyState 在 chrome 中不起作用

无法访问,内部,资源文件?

如何从以特定名称开头的 appsettings 键中获取所有值并将其传递给任何数组?

使用 IIS 的 ASP.NET 调试超时

如何使用 javascript 调用 ASP.NET c# 方法

HttpContext.Current.Cache.Insert 和 HttpContext.Current.Cache.Add 有什么区别

您如何以编程方式填写表格并发布网页?

如何设置默认页面 asp.net

使用 JObject 所需的库名称是什么?

LINQ:使用 Lambda 表达式获取 CheckBoxList 的所有选定值

如何配置 ELMAH 以使用 Windows Azure?我在 Elmah.axd 上得到 404

如何防止 XXE 攻击(.NET 中的 XmlDocument)

用于呈现

对于 DB ID,需要一个较小的 GUID 替代方案,但对于 URL 仍然是唯一且随机的

覆盖静态方法

使用 Web.Config 转换的高级任务

ASP.net 判断页面是 http 还是 https