【问题标题】:How to create hierarchy in json string from string array?如何从字符串数组在 json 字符串中创建层次结构?
【发布时间】:2018-02-10 11:50:34
【问题描述】:

我正在尝试为如下层次结构生成 json 字符串:

Company(select * from Company)
    Department(select * from Department)
        Employee(select * from Employee)

上述每个查询都将返回如下字段:

Company Fields -  (Id,Name,Location)
Department Fields - (Id,Name,CompanyId)
Employee Fields - (Id,Name,DepartmentId)

现在我正在尝试为上述实体生成 JSON 字符串,如下所示:

预期输出:

{
  "Id": "",
  "Name": "",
  "Location": "",
  "Department": 
        {
           "Id": "",
           "Name": "",
           "CompanyId": "",
           "Employee" : 
               {
                  "Id": "",
                  "Name": "",
                  "DepartmentId": "", 
               }
        }
}

代码:

public string GetData(Child model,List<Parent> parents)
        {
           var fields = new List<string[]>();
           if (parents != null)
           {
                foreach (var parent in parents)
                {
                        var columns = GetColumns(parent); //returns string[] of columns
                        fields.Add(columns);
                 }
            }
            fields.Add(GetColumns(model));
            string json = JsonConvert.SerializeObject(fields.ToDictionary(key => key, v => string.Empty),
                                        Formatting.Indented);
            return json;
        }

现在,当我没有任何父母并且想为唯一的孩子生成 json 字符串时,下面的代码可以正常工作:

string json = JsonConvert.SerializeObject(fields.ToDictionary(key => key, v => string.Empty),Formatting.Indented)

输出:

{
  "Id": "",
  "Name": "",
  "Location": "",
}

但现在我想用任何这种内置方式为我的层次结构生成 JSON。

我知道我可以循环、追加和创建 json 字符串,但我想以更好的方式做到这一点,就像我为我的孩子所做的那样。

更新:

public class Child
{
    public string Name { get; set; } // Contains Employee
   //Other properties and info related to process sql query and connection string
}


public class Parent
{
    public string Name { get; set; } // Contains Company,Department.
    public string SqlQuery { get; set; } // query related to Company and Department.
    //Other properties and info related to connection string
}

【问题讨论】:

  • 你也可以分享你的孩子和家长课程吗?
  • @lancew我没有任何问题可以共享子类和父类,但是我对这两个类有不同的结构,因此我正在做一些操作,然后我的 GetColumns 方法基于这些操作返回列。因此我没有共享 Child 和 Parent 类的代码,因为我认为它在这里不相关
  • 好吧,也许他们不是。据我所知,您的 GetData 函数似乎具有向后的参数。不应该有一个父母有很多孩子吗?另外,我不确定是否有任何简单的方法可以序列化字符串 List 以获得所需的输出。您必须将列及其值添加到嵌套字典或其他内容中。
  • @lancew 我只希望我的所有值都为空,您能否为您所说的内容添加更多详细信息,例如嵌套字典

标签: c# json linq json.net


【解决方案1】:

我创建了一个类,该类在子父结构中保存与您建议的信息类似的信息。我还添加了一个自定义的小解析器,它可以递归地工作。也许这就是您所需要的和/或为您提供解决问题所需的想法。

我还通过添加尖括号 ("[ ]" ) 稍微改变了输出。我认为这就是您对多个孩子所需要的。至少这是我在下面发布的 JSON 验证器告诉我的。如果您不需要/不想要它们,只需在解析器中删除它们即可。

我不认为你可以使用你在你的例子中使用的解析器,而没有像我在之前的答案中展示的某种形式的额外字段,因为这些解析器通常将属性名称作为字段,我猜你没有想要在运行时动态创建类。

我也不认为您可以创建与列表、数组或字典的父-子-子-子...关系的动态深度,因为这些结构很快就有了深度正如他们所声明的那样。

类:

public class MyJsonObject
{
    public List<string> Columns = new List<string>();

    public string ChildName;
    public List<MyJsonObject> Children = new List<MyJsonObject>(); 
}

解析器:

class JsonParser
{
    public static string Parse(MyJsonObject jsonObject)
    {
        string parse = "{";

        parse += string.Join(",", jsonObject.Columns.Select(column => $"\"{column}\": \"\""));

        if (!string.IsNullOrEmpty(jsonObject.ChildName))
        {
            parse += $",\"{jsonObject.ChildName}\":";

            parse += $"[{string.Join(",", jsonObject.Children.Select(Parse))}]";
        }

        parse += "}";

        return parse;
    }
}

用法:

class Program
{
    static void Main(string[] args)
    {
        MyJsonObject company = new MyJsonObject();
        company.ChildName = "Department";
        company.Columns.Add("Id");
        company.Columns.Add("Name");
        company.Columns.Add("Location");

        MyJsonObject department = new MyJsonObject();
        department.ChildName = "Employee";
        department.Columns.Add("Id");
        department.Columns.Add("Name");
        department.Columns.Add("CompanyId");

        MyJsonObject employee1 = new MyJsonObject();
        employee1.Columns.Add("Id");
        employee1.Columns.Add("Name");
        employee1.Columns.Add("DepartmentId");

        MyJsonObject employee2 = new MyJsonObject();
        employee2.Columns.Add("Id");
        employee2.Columns.Add("Name");
        employee2.Columns.Add("DepartmentId");

        company.Children.Add(department);
        department.Children.Add(employee1);
        department.Children.Add(employee2);

        var json = JsonParser.Parse(company);
    }
}

输出和链接到 JSON 验证器:

https://jsonformatter.curiousconcept.com/

{  
   "Id":"",
   "Name":"",
   "Location":"",
   "Department":[
      {  
         "Id":"",
         "Name":"",
         "CompanyId":"",
         "Employee":[
            {  
               "Id":"",
               "Name":"",
               "DepartmentId":""
            },
            {  
               "Id":"",
               "Name":"",
               "DepartmentId":""
            }
         ]
      }
   ]
}

【讨论】:

  • 我已经在我的回答中写了我的最终预期输出 json 字符串。我还指定我只对每个父母和孩子进行 sql 查询,在执行这些查询后我得到列数组(字符串 [])。您的输出 json 与我预期的输出 json 不匹配。我没有每个列的值。我只有列的名称
  • 赞成您为帮助我所做的努力,但部门 json 和员工 json 仍然存在问题,我只想要一个对象而不是数组。请参阅我预期的 json 输出中的 Employee 和 Department 属性
  • 那么你基本上想要的是每个公司只有一个部门,每个部门只有一个员工?
  • 看这里我不关心每个公司只有一个部门和员工的记录。现在即使 1 家公司将有超过 1 个部门并且 1 个部门将有超过 1 名员工,我仍然应该得到我在预期输出中显示的 json 结构。因此我不关心公司、员工和部门之间的一对多关系。
  • 对不起,如果我在这里看起来有点慢,但它与您提出的预期输出究竟有什么不同?我在这里看到的唯一区别是孩子的数量和尖括号“[]”。到底有什么不合适的?
【解决方案2】:

也许我错过了什么。如果你在 heirachy 中创建你需要的类,用数据实例化它们,然后序列化它们,就会为你创建结构。

using System.Web.Script.Serialization;

public class Employee 
{
   public int Id {get; set; }
   public string Name {get; set; }
   public int DepartmentId {get; set; }   
}

public class Department 
{
   public int Id {get; set; }
   public string Name {get; set; }
   public string CompanyId {get; set; }
   public List<Employee> {get; set;}
}

public class Company {
   public int Id {get; set; }
   public string Name {get; set; }
   public string Location {get; set; }
   public List<Department> {get; set;}
}

var myCompany = new Company();
// add departments and employees

var json = new JavaScriptSerializer().Serialize(myCompany);

【讨论】:

  • 很抱歉,但问题是我没有固定的表格。这只是我在问题中展示的一个示例(公司、部门、员工)。所以我不能预定义类
【解决方案3】:

您可以使用动态

//here your database
dynamic[] company = new object[] { new { Name = "Company1", DepartmentId = 1 }, new { Name = "Company2", DepartmentId = 2 } };
dynamic[] department = new object[] { new { DepartmentId = 1, Name = "Department1" }, new { DepartmentId = 2, Name = "Department2" } };

//select from database
var data = from c in company
    join d in department on c.DepartmentId equals d.DepartmentId
    select new {Name = c.Name, Department = d};

var serialized = JsonConvert.SerializeObject(data);

结果:

[
  {
    "Name": "Company1",
    "Department": {
      "DepartmentId": 1,
      "Name": "Department1"
    }
  },
  {
    "Name": "Company2",
    "Department": {
      "DepartmentId": 2,
      "Name": "Department2"
    }
  }
]

【讨论】:

  • 很抱歉,您在这里提前修复了属性,但我没有任何固定的表。所以基本上我正在接收一个查询并使用连接字符串信息执行它。这就是我使用字符串的原因数组,因为我没有关于字段的任何信息。这取决于查询。查询也会有连接
  • 所以你执行查询并得到什么?无论如何你可以创建一个动态对象然后序列化它
  • 我执行了一个查询,我得到了列的字符串 []。GetColumns 执行该方法并返回我有列名称的字符串 [] 数组。
  • @Learning 也许 Timur 正在做一些动态的事情。您可以使用 ExpandObject 并动态创建您的字段,然后尝试按照 Timur 的建议对它们进行序列化(请参阅stackoverflow.com/questions/6196022/…
【解决方案4】:

好的,让我们试试这样。首先,我理解你的难题:你有父母和孩子的属性数组,你需要将它转换为 json 对象。 重点在这里:

public static ExpandoObject DicTobj(Dictionary<string, object> properties)
        {
            var eo = new ExpandoObject();
            var eoColl = (ICollection<KeyValuePair<string, object>>)eo;

            foreach (var childColumn in properties)
                eoColl.Add(childColumn);

            return eo;
        }

U 使用 dynamicExpandoObject 将字典转换为对象

其他代码很简单:你使用 dynamic 类型将所有对象放在一个 并将其序列化。

完整代码:

public static Child Child1 { get; set; } = new Child
        {
            Name = "Child1"
        };

        public static Parent Parent1 { get; set; } = new Parent
        {
            Name = "Parent1"
        };

        public static Parent Parent2 { get; set; } = new Parent
        {
            Name = "Parent2"
        };

        private static void Main(string[] args)
        {
            var result = GetData(Child1, new List<Parent> {Parent1, Parent2});
            Console.WriteLine(result);
        }

        /// <summary>
        ///     This is the magic: convert dictionary of properties to object with preperties
        /// </summary>
        public static ExpandoObject DicTobj(Dictionary<string, object> properties)
        {
            var eo = new ExpandoObject();
            var eoColl = (ICollection<KeyValuePair<string, object>>) eo;

            foreach (var childColumn in properties)
                eoColl.Add(childColumn);

            return eo;
        }

        public static string GetData(Child model, List<Parent> parents)
        {
            var childColumns = GetColumns(model);
            dynamic child = DicTobj(childColumns);

            var parentsList = new List<object>();
            foreach (var parent in parents)
            {
                var parentColumns = GetColumns(parent);
                var parentObj = DicTobj(parentColumns);
                parentsList.Add(parentObj);
            }

            child.Parents = parentsList;

            return JsonConvert.SerializeObject(child);
        }


        /// <summary>
        ///     this is STUB method for example
        ///     I change return type from string[] to Dictionary[columnName,ColumnValue], becouse u need not only column names, but
        ///     with it values, i gues. If not, look commented example at the end of this method
        /// </summary>
        public static Dictionary<string, object> GetColumns(object model)
        {
            var result = new Dictionary<string, object>();
            if (model == Child1)
            {
                result.Add("Id", "1");
                result.Add("Name", "Child1");
                result.Add("Location", "SomeLocation");
            }

            if (model == Parent1)
            {
                result.Add("Id", "2");
                result.Add("Name", "Parent1");
                result.Add("SomeProperty1", "SomeValue1");
            }

            if (model == Parent2)
            {
                result.Add("Id", "3");
                result.Add("Name", "Parent1");
                result.Add("SomeProperty3", "SomeValue2");
            }

            //if u have only columNames and dont need values u can do like this
            //var columns = new[] {"Id", "Name", "SomeProperty1"};//this u get from DB
            //return columns.ToDictionary(c => c, c => new object());

            return result;
        }
    }

    public class Child
    {
        public string Name { get; set; } // Contains Employee
        //Other properties and info related to process sql query and connection string
    }

    public class Parent
    {
        public string Name { get; set; } // Contains Company,Department.

        public string SqlQuery { get; set; } // query related to Company and Department.
        //Other properties and info related to connection string
    }

结果输出:

{
  "Id": "1",
  "Name": "Child1",
  "Location": "SomeLocation",
  "Parents": [
    {
      "Id": "2",
      "Name": "Parent1",
      "SomeProperty1": "SomeValue1"
    },
    {
      "Id": "3",
      "Name": "Parent1",
      "SomeProperty3": "SomeValue2"
    }
  ]
}

【讨论】:

  • 感谢您的回答,但您的 json 字符串与我预期的输出 json 字符串不匹配。所以基本上 Parent2 应该在 Parent1 内(应该嵌套而不是数组)。注意 Employee 如何在我的部门内和我不想要值。我只有列的名称
【解决方案5】:

即使你没有固定的结构,你也可以传递任何类型的对象:

Newtonsoft.Json.JsonConvert.SerializeObject(new yourCustomObject)

通过使用这个。

【讨论】:

  • 这里的问题是生成该结构,如我预期的输出所示。如何在不循环的情况下有效地生成该结构。
  • 以字符串为例,它仍然接受通过字符串构造的json
  • 可以动态构建字符串
【解决方案6】:

获得此结果的最佳方法
- 您必须创建一个具有所有类关系的新类。然后使用 Newtonsoft.Json.JsonConvert.SerializeObject(新组织)
让我们创建一个名为 organization 的新类。在 Json 中添加您想要查看的关系。然后使用 JsonConvert 转换成 JSON。
或者你可以使用下面的动态循环

//here your database<br/>
dynamic[] company = new object[] { new { Name = "Company1", DepartmentId = 1 }, new { Name = "Company2", DepartmentId = 2 } };

动态[] 部门 = 新对象 [] { 新 { 部门 ID = 1,名称 = “部门 1”},新 { 部门 ID = 2,名称 = “部门 2”} };

//select from database<br/>
var data = from c in company
    join d in department on c.DepartmentId equals d.DepartmentId
    select new {Name = c.Name, Department = d};


var serialized = JsonConvert.SerializeObject(data);

【讨论】:

    猜你喜欢
    • 2019-12-18
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多