使用Json.Net解析两个语义相同的层次结构文档并失败

时间:2017-03-25 09:51:27

标签: json parsing json.net hierarchy

我有两个JSON文档,它们代表相同的层次结构和内容。我可以在两个文档之间看到的唯一区别是键值对的排序是不同的。一个文档按照我的预期进行解析,而另一个文档则没有。

我正在使用“保留引用处理”,因此节点应引用其父节点。 (测试中的变量“hierarchyTwoNode”是未设置其Parent属性的文档)。我已经包含了一个测试(can be found here)来证明这一点。以下是工作JSON的简化版本:

{
  "Root": {
    "$id": "1",
    "Id": "1472459628771017730",
    "Type": "cras",
    "Content": {
      "Name": "lorem"
    },
    "Parent": null,
    "Children": [
      {
        "$id": "2",
        "Id": "1472459628812960771",
        "Type": "morbi",
        "Content": {
          "Name": "ipsum dolor"
        },
        "Parent": {
          "$ref": "1"
        }
      }
    ]
  }
}

失败的JSON:

{
  "Root": {
    "Parent": null,
    "$id": "1",
    "Children": [
      {
        "Parent": {
          "$ref": "1"
        },
        "$id": "2",
        "Content": {
          "Name": "ipsum dolor"
        },
        "Type": "morbi",
        "Id": "1472459628812960771"
      }
    ],
    "Content": {
      "Name": "lorem"
    },
    "Type": "cras",
    "Id": "1472459628771017730"
  }
}

有人可以让我知道发生了什么吗?

2 个答案:

答案 0 :(得分:2)

  

有人可以让我知道发生了什么吗?

基本上,正在发生的事情是,在其中一个JSON字符串上,您的元数据属性放在之后第一个实际属性。

所有$xxx属性都是元数据,必须放在对象/子对象的开头。

此限制的原因如下:

JSON.Net的

if we look at the internals,在执行元数据查找时,我们可以看到,只要我们读取不是元数据的属性,我们就会停止元数据查找。

如果JSON文件非常大,我只能公平地假设它是用于计算和内存优化的。

要使代码正常工作,只需在对象的开头放置以$开头的所有属性,它就像魅力一样。

答案 1 :(得分:2)

正如@Fabio Salvalai正确推断的那样,Json.Net通常希望任何元数据属性(例如$id$type)首先出现在每个对象中,以便在反序列化中获得最佳效率。如果元数据没有首先出现,那么Json.Net认为它不存在。这就是为什么在重新排序属性时会得到不同的结果。

幸运的是,Json.Net提供MetadataPropertyHandling设置以允许它处理这种情况。如果您将MetadataPropertyHandling设置为ReadAhead,则应解决您的问题。请注意,如果您的JSON很大,这可能会对性能产生影响。

JsonSerializerSettings settings = new JsonSerializerSettings
{
    MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead
};

var hierarchyOne = JsonConvert.DeserializeObject<Hierarchy>(HierarchyOne, settings);
var hierarchyTwo = JsonConvert.DeserializeObject<Hierarchy>(HierarchyTwo, settings);