简体   繁体   中英

How do I construct a nested, strongly typed object from a flat ("non-normalized") array?

I'm writing a .NET service to consume (GET) a JSON array from a web service containing data, which if in a database, would be normalized into separate related tables. The JSON array will be coming in with each element repeating the parent data properties and only the childrens' properties will change from element to element. So here's what the JSON object will look like:

[  {
    "parentID": 123,
    "parentName": "Parent Name",
    "childID": 1,
    "childName": "First Child",
    "subChildID": null,
    "subChildName": null
},
{
    "parentID": 123,
    "parentName": "Parent Name",
    "childID": 2,
    "childName": "Second Child",
    "subChildID": null,
    "subChildName": null
},
{
    "parentID": 123,
    "parentName": "Parent Name",
    "childID": 3,
    "childName": "Third Child",
    "subChildID": 100,
    "subChildName": "First Subchild of the third child"
},
{
    "parentID": 123,
    "parentName": "Parent Name",
    "childID": 4,
    "childName": "Third child",
    "subChildID": 101,
    "subChildName": "Second subchild of the third child"
}]

But I need to transform this array (hopefully with Newtonsoft or Linq libraries?) into a .NET object that will look something like this:

public class ParentObject
{
    public int parentID { get; set; }
    public string parentName { get; set; }
    public List<ChildObject> children { get; set; }

    private class ChildObject
    {
        public int childID { get; set; }
        public string childName { get; set; }
        public List<SubChildObject> subChildren { get; set; }

        private class SubChildObject
        {
            public int subChildID { get; set; }
            public string subChildName { get; set; }
        }
    }
}

I've seen examples of doing the opposite; flattening a nested object into a list-like object, but not what I'm looking for. Again, I was hoping it could be accomplished by the Json libraries of Newtonsoft or plain Linq. Thanks.

I'm sure this can be done with Linq, but it's easily doable with just a loop, as this example code shows:

List<ParentObject> CreateEntities(string json)
{
    var entities = JsonConvert.DeserializeObject<List<RootObject>>(json);
    List<ParentObject> parents = new List<ParentObject>();

    foreach (var entity in entities)
    {
        if (parents.Any(p => p.parentID == entity.parentID))
        {
            var parent = parents.Single(p => p.parentID == entity.parentID);

            if (parent.children.Any(c => c.childID == entity.childID))
            {
                var child = parent.children.Single(c => c.childID == entity.childID);
                if (entity.subChildID.HasValue)
                {
                    child.subChildren.Add(new ParentObject.ChildObject.SubChildObject
                    {
                        subChildID = entity.subChildID.Value,
                        subChildName = entity.subChildName
                    });
                }
            }
            else
            {
                var newChild = (new ParentObject.ChildObject
                {
                    childID = entity.childID,
                    childName = entity.childName,
                    subChildren = new List<ParentObject.ChildObject.SubChildObject>()
                });


                if (entity.subChildID.HasValue)
                {
                    newChild.subChildren.Add(new ParentObject.ChildObject.SubChildObject
                    {
                        subChildID = entity.subChildID.Value,
                        subChildName = entity.subChildName
                    });
                }

                parent.children.Add(newChild);
            }
        }
        else
        {
            var newParent = new ParentObject
            {
                parentID = entity.parentID,
                parentName = entity.parentName,
                children = new List<ParentObject.ChildObject>
                {
                    new ParentObject.ChildObject
                    {
                        childID = entity.childID,
                        childName = entity.childName,
                        subChildren = new List<ParentObject.ChildObject.SubChildObject>()
                    }
                }
            };

            if (entity.subChildID.HasValue)
            {
                newParent.children.Single().subChildren.Add(new ParentObject.ChildObject.SubChildObject
                {
                    subChildID = entity.subChildID.Value,
                    subChildName = entity.subChildName
                });
            }

            parents.Add(newParent);
        }
    }

    return parents;
}

public class RootObject
{
    public int parentID { get; set; }
    public string parentName { get; set; }
    public int childID { get; set; }
    public string childName { get; set; }
    public int? subChildID { get; set; }
    public string subChildName { get; set; }
}

public class ParentObject
{
    public int parentID { get; set; }
    public string parentName { get; set; }
    public List<ChildObject> children { get; set; }

    public class ChildObject
    {
        public int childID { get; set; }
        public string childName { get; set; }
        public List<SubChildObject> subChildren { get; set; }

        public class SubChildObject
        {
            public int subChildID { get; set; }
            public string subChildName { get; set; }
        }
    }
}

Output:

在此处输入图片说明

Note:

This code handles multiple parents in one response, as although your question suggests that there is only one parent per response I wasn't completely sure.

Both "Third childs" objects in JSON have different names "Third child" and "Third C hild" and different IDs 3 and 4, I made these fields same to get better result on output.

Also I used int? for IDs, because JSON has nullable int fields and Tuples (C# 7.0) instead anonymous objects.

You can remove Where filters to collect objects with null IDs also.

Let me know if you have questions or need fixes, here is the code:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            string json = @"[
                             {
                                 ""parentID"": 123,
                                 ""parentName"": ""Parent Name"",
                                 ""childID"": 1,
                                 ""childName"": ""First Child"",
                                 ""subChildID"": null,
                                 ""subChildName"": null
                             },
                             {
                                 ""parentID"": 123,
                                 ""parentName"": ""Parent Name"",
                                 ""childID"": 2,
                                 ""childName"": ""Second Child"",
                                 ""subChildID"": null,
                                 ""subChildName"": null
                             },
                             {
                                 ""parentID"": 123,
                                 ""parentName"": ""Parent Name"",
                                 ""childID"": 3,
                                 ""childName"": ""Third child"",
                                 ""subChildID"": 100,
                                 ""subChildName"": ""First Subchild of the third child""
                             },
                             {
                                 ""parentID"": 123,
                                 ""parentName"": ""Parent Name"",
                                 ""childID"": 3,
                                 ""childName"": ""Third child"",
                                 ""subChildID"": 101,
                                 ""subChildName"": ""Second subchild of the third child""
                             }
                            ]";

            JArray jarr = JArray.Parse(json);

            IEnumerable<ParentObject> parents = jarr.GroupBy(t => ((int?)t["parentID"], (string)t["parentName"]))
                                                    .Select(pg => new ParentObject
                                                    {
                                                        parentID = pg.Key.Item1,
                                                        parentName = pg.Key.Item2,
                                                        children = pg
                                                    .GroupBy(t => ((int?)t["childID"], (string)t["childName"]))
                                                    .Select(cg => new ParentObject.ChildObject()
                                                    {
                                                        childID = cg.Key.Item1,
                                                        childName = cg.Key.Item2,
                                                        subChildren = cg
                                                    .Select(t => new ParentObject.ChildObject.SubChildObject()
                                                    {
                                                        subChildID = (int?)t["subChildID"],
                                                        subChildName = (string)t["subChildName"]
                                                    }).Where(s => s.subChildID != null).ToList()
                                                    }).Where(c => c.childID != null).ToList()
                                                    }).Where(p => p.parentID != null).ToList();

            json = JsonConvert.SerializeObject(parents, Formatting.Indented);

            Console.WriteLine(json);

            Console.ReadKey();
        }
    }

    public class ParentObject
    {
        public int? parentID { get; set; }
        public string parentName { get; set; }
        public List<ChildObject> children { get; set; }

        public class ChildObject
        {
            public int? childID { get; set; }
            public string childName { get; set; }
            public List<SubChildObject> subChildren { get; set; }

            public class SubChildObject
            {
                public int? subChildID { get; set; }
                public string subChildName { get; set; }
            }
        }
    }
}

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