【问题标题】:C# .NET: Deserialize json treeC# .NET:反序列化 json 树
【发布时间】:2015-10-18 18:56:47
【问题描述】:

晚上好,

对于我的具体问题,我必须创建一个菜单(更准确地说是菜单树)。因此,我决定使用具有以下结构的复合设计模式:

  • IMenuComponent(定义一些属性等的接口)
  • 菜单(包含 IMenuComponent 列表)
  • MenuEntry(一片叶子)

所以,我必须在知道返回路径的同时浏览它。显而易见的答案是拥有一个“父”属性。

我有以下 json 树:

{
  "Guid": "08967257-9306-4717-a76a-e1a4f0050505",
  "Parent": null,
  "Title": "Main Menu",
  "Message": "A sample message",
  "Elements": [
    {
      "$type": "Menu",
      "Guid": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
      "Parent": "08967257-9306-4717-a76a-e1a4f0050505",
      "Title": "Option 1",
      "Message": "Another sample message",
      "Elements": [
        {
          "$type": "MenuEntry",
          "Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
          "Title": "Entry 1",
          "Message": "Another sample message"
        },
        {
          "$type": "MenuEntry",
          "Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
          "Title": "Entry 2",
          "Message": "Another sample message"
        },
        {
          "$type": "MenuEntry",
          "Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
          "Title": "Entry 3",
          "Message": "Another sample message"
        }
      ]
    },
    {
      "$type": "MenuEntry",
      "Parent": "08967257-9306-4717-a76a-e1a4f0050505",
      "Title": "Option 2",
      "Message": "Another sample message"
    }
  ]
}

这与反序列化本身无关,因为它可以正常工作。

我的问题是在将文件反序列化为“菜单”(根目录)后,我必须访问父母。 我可以想到两种方法:

  • 在运行时使用其 Guid 查找“父级” - 根据树的大小,性能可能会很差;
  • 在 'Menu' 和 'MenuEntry' 类中添加一个 'Menu' 属性来存储 'parent' 并在从 json 反序列化后通过查找相应的 Guid 来填充它。基本上,我会在应用程序启动时将它放在一起,防止在运行时查找。这在性能方面也可能很糟糕。

怎么走?我该怎么做?

附带说明,我使用的是Newtonsoft.Json

感谢您的帮助!

【问题讨论】:

  • Guid 在应用中有什么意义吗?还是它只是您的序列化逻辑的一部分?如果是后者,为什么要拥有它?树结构已经是数据中固有的;对其进行第二个表示几乎没有什么好处,并且可能与实际结构发生冲突。
  • 即使树结构已经存在于数据中(即使在反序列化之后),知道“父级”的唯一方法是存储一个 id,在这种情况下是一个 Guid。拥有“菜单”类型的“父”将具有循环引用,这首先是不可能序列化的。但是,如我的帖子所述,在反序列化后填充该属性是可能的。
  • 实际上,在第二个选项中,我必须遍历整个树并比较 Guid。既然我这样做了,我可以即时存储“菜单”属性,而不需要指导。我会试一试,稍后再发布结果。
  • @Apidcloud - 仅供参考:如果您使用PreserveReferencesHandling 设置,可以在 Json.Net 中序列化循环引用。但是,这将导致 Json.Net 向 JSON 写入额外的 $id$ref 属性,这可能会或可能不会被您接受。当您反序列化具有这些额外元数据的 JSON 时,Json.Net 会自动为您重新连接所有对象引用。
  • 我不知道那个!只试了ReferenceLoopHandling,没用。由于它是应用程序资产,我相信这样做是可以的。谢谢

标签: c# json tree json.net


【解决方案1】:

@Ian Mercer 的评论实际上帮助我找到了一个简单的解决方案(使用我上面帖子中描述的第二种方式)。

第二种方式并不真正依赖Guids。反序列化之后,我可以遍历每个菜单集合(我可能会这样做)并直接引用“父级”。

private void ConnectTree(Menu menu)
{
    foreach (IMenuComponent component in menu.Elements) {
        if (component is Menu) {
            (component as Menu).ParentMenu = menu;
            ConnectTree (component as Menu);
        } 
        else if (component is MenuEntry) {
            (component as MenuEntry).ParentMenu = menu;
        }
    }
}

只需拨打ConnectTree(_deserializedMenu)

编辑: @Brian Rogers 刚刚提到了来自 json net 的 PreserveReferencesHandling 属性。最初的问题是关于 json 不支持循环引用,但这解决了它 - 它与使用 Guids 的逻辑相同。有多种解决方案,知道这一点总是很好。

谢谢!

【讨论】:

    猜你喜欢
    • 2018-06-13
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多