在 C# 中用 JSON.NET 展平嵌套的 JSON

Flatten nested JSON with JSON.NET in C#

我通过具有相应层次结构的 WebApi 收到 JSON 格式的物料清单。 层级或嵌套可以是任意深度。

物料清单示例如下所示:

{
   "Quantity":0,
   "QuantityUnit":"pcs",
   "PartNumber":"12345",
   "Parent":"",
   "Children":[
      {
         "Quantity":1,
         "QuantityUnit":"pcs",
         "PartNumber":"88774",
         "Parent":"12345",
         "Children":[
            {
               "Quantity":1,
               "QuantityUnit":"pcs",
               "PartNumber":"42447",
               "Parent":"88774"
            },
            {
               "Quantity":0.420,
               "QuantityUnit":"kg",
               "PartNumber":"12387",
               "Parent":"88774"
            }
         ]
      }
   ]
}

如何在 C# 中使用 JSON.NET 将此嵌套结构解析为简单结构?

我想将其转换为:

[
   {
      "Quantity":0,
      "QuantityUnit":"pcs",
      "PartNumber":"12345",
      "Parent":""
   },
   {
      "Quantity":1,
      "QuantityUnit":"pcs",
      "PartNumber":"88774",
      "Parent":"12345"
   },
   {
      "Quantity":1,
      "QuantityUnit":"pcs",
      "PartNumber":"42447",
      "Parent":"88774"
   },
   {
      "Quantity":0.420,
      "QuantityUnit":"kg",
      "PartNumber":"12387",
      "Parent":"88774"
   }
]

对于反序列化,我使用以下 class:

public class Bom
{
    public class TopLevel
    {
        public double Quantity { get; set; }
        public string QuantityUnit { get; set; }
        public string PartNumber { get; set; }
        public string Parent { get; set; }
        public List<Item> Children { get; set; }
    }

    public class Item
    {
        public double Quantity { get; set; }
        public string QuantityUnit { get; set; }
        public string PartNumber { get; set; }
        public string Parent { get; set; }
    }

    public double Quantity { get; set; }
    public string QuantityUnit { get; set; }
    public string PartNumber { get; set; }
    public string Parent { get; set; }
    public IList<TopLevel> Children { get; set; }
}

此外,我使用此代码将 JSON 反序列化为一个对象:

Bom bom = JsonConvert.DeserializeObject<Bom>(File.ReadAllText(jsonPath));

反序列化原始列表,用 Enumerable.SelectMany 将其展平,然后序列化结果序列。

首先让我们定义一个映射器

JObject Map(JObject source)
{
    var result = (JObject)source.DeepClone();
    result.Remove("Children");
    return result;
}
  • 它只是克隆对象并删除 Children 属性

接下来我们定义一个递归函数来累加JObjects

void Flatten(JArray children, JArray accumulator)
{
    if (children == null) return;
    foreach (JObject child in children)
    {
        accumulator.Add(Map(child));
        Flatten((JArray)child["Children"], accumulator);
    }
}

最后让我们利用它们

var semiParsed = JObject.Parse(json);

var accumulator = new JArray();
accumulator.Add(Map(semiParsed));
Flatten((JArray)semiParsed["Children"], accumulator);

accumulatorToString 调用将 return 这

[
  {
    "Quantity": 0,
    "QuantityUnit": "pcs",
    "PartNumber": "12345",
    "Parent": ""
  },
  {
    "Quantity": 1,
    "QuantityUnit": "pcs",
    "PartNumber": "88774",
    "Parent": "12345"
  },
  {
    "Quantity": 1,
    "QuantityUnit": "pcs",
    "PartNumber": "42447",
    "Parent": "88774"
  },
  {
    "Quantity": 0.42,
    "QuantityUnit": "kg",
    "PartNumber": "12387",
    "Parent": "88774"
  }
]

更新#1

如果您的源 json 包含很深的层次结构(比方说超过 5 层),那么 DeepClone 并不是很有效,因为您正在复制整个子树。

要解决此问题,您只需重写 Map 函数

JObject Map(JObject source)
=> JObject.FromObject(new
{
    Quantity = (double)source["Quantity"],
    QuantityUnit = (string)source["QuantityUnit"],
    PartNumber = (string)source["PartNumber"],
    Parent = (string)source["Parent"]
});