简体   繁体   中英

C# .NET: Deserialize json tree

Good evening,

For my specific problem, I have to create a menu (a tree of menus more exactly). Thus, I decided to use Composite Design Pattern with the following structure:

  • IMenuComponent (an interface defining some properties and so on)
  • Menu (contains a list of IMenuComponent)
  • MenuEntry (a leaf)

So, I'll have to navigate through it while knowing the way back up. The obvious answer is having a 'parent' property.

I have the following json tree:

{
  "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"
    }
  ]
}

This isn't about the deserialization itself, since that's working as it should.

My problem is that I'll have to access the parents after deserializing the file to a 'Menu' (the root). I can think of two ways:

  • Find a 'parent' in runtime using its Guid - can be bad performance-wise depending on tree size;
  • Add a 'Menu' property to both 'Menu' and 'MenuEntry' classes to store the 'parent' and fill it, by finding the corresponding Guid, after deserializing from json. Basically, I'd put it together as soon as the application starts, preventing the find in runtime. It could be bad performance-wise as well.

What's the way to go? And how should I do it?

As a side note, I'm using Newtonsoft.Json

Thanks for the help!

@Ian Mercer comment actually helped me figure out a simple solution (using the 2nd way described in my post above).

The 2nd way doesn't really rely on Guids. After the deserialize, I can just iterate over each menu collection (as I probably would anyway) and give the reference to the 'parent' directly.

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

Just need to call ConnectTree(_deserializedMenu)

Edit: @Brian Rogers just mentioned PreserveReferencesHandling property from json net. The original problem was all about json not supporting circular references, but that solves it - it's the same logic as using Guids. There are multiple solutions, which is always good to know.

Thanks!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM