C# 将带有数组的json结构扁平化为多个不带数组的扁平对象

C# 将带有数组的json结构扁平化为多个不带数组的扁平对象,c#,json,recursion,json.net,C#,Json,Recursion,Json.net,我不确定我是否100%正确地描述了主题中的问题,但我相信这些例子会起到作用 我的JSON结构如下所示(注意:这可能会发生变化的可能性很小,所以我需要向前倾斜到通用解决方案) 一张包含多个行项目的发票: 这是所需的结果数据结构(具有重复数据和不同行项目信息的多张发票): 因此,“发票”中的每个“行项目”都应该“产生”一个新的发票,并带有重复的其他元素 结果数据结构的微小变化是可以接受的,我可以围绕它调整我的代码。 我一直在使用几个类似的问题,例如: (等等)。我相信这是最接近的解决办法?但

我不确定我是否100%正确地描述了主题中的问题,但我相信这些例子会起到作用

我的JSON结构如下所示(注意:这可能会发生变化的可能性很小,所以我需要向前倾斜到通用解决方案)

一张包含多个行项目的发票:

这是所需的结果数据结构(具有重复数据和不同行项目信息的多张发票):

因此,“发票”中的每个“行项目”都应该“产生”一个新的发票,并带有重复的其他元素

结果数据结构的微小变化是可以接受的,我可以围绕它调整我的代码。 我一直在使用几个类似的问题,例如:

  • (等等)。我相信这是最接近的解决办法?但不确定是否有更好的方法
更多背景资料,我需要这个CSV导出。所以结果集应该是生成的CSV中的两行


如有任何提示,我们将不胜感激。谢谢。

您可以使用如下功能:

//Pass in the name of the array property you want to flatten
public string FlattenJson(string input, string arrayProperty)
{
    //Convert it to a JObject
    var unflattened = JsonConvert.DeserializeObject<JObject>(input);

    //Return a new array of items made up of the inner properties
    //of the array and the outer properties
    var flattened = ((JArray)unflattened[arrayProperty])
        .Select(item => new JObject(
            unflattened.Properties().Where(p => p.Name != arrayProperty), 
            ((JObject)item).Properties()));

    //Convert it back to Json
    return JsonConvert.SerializeObject(flattened);
}
var flattenedJson = FlattenJson(inputJson, "lineItems");

如果能够反序列化/序列化为强类型类,则可以使用自定义的
JsonConverter
。 我认为发票信息应该在某种半结构化对象中,因此这应该是可行的:

public class Invoice
{
    public string ContactName { get; set; }
    public List<Item> LineItems { get; set; } = new List<Item>();
    public string InvoiceNumber { get; set; }
}

public class Item
{
    public double Quantity { get; set; }
    public string Description { get; set; }
}
要使用此转换器,请在序列化时提供它

        var invoice = JsonConvert.DeserializeObject<Invoice>(inputJson);
        var outputJson = JsonConvert.SerializeObject(invoice, new InvoiceFlattener());
var invoice=JsonConvert.DeserializeObject(inputJson);
var outputJson=JsonConvert.SerializeObject(invoice,new InvoiceFlatter());
正如您可能已经解决的那样,反序列化时此转换器不起作用,但是如果这是一项要求,您可以在ReadJson converter方法中编写逻辑。 缺点是,如果
发票
类的结构发生变化,您将需要维护转换器。但它让我们置身于一个强类型的世界

,有了外部库(一个开源库),您只需几行代码就可以将JSON转换为预期的CSV格式

string json = @"{
    ""contactName"": ""Company"",
    ""lineItems"": [
     {
        ""quantity"": 7.0,
        ""description"": ""Beer No* 45.5 DIN KEG""
     },
     {
        ""quantity"": 2.0,
        ""description"": ""Beer Old 49.5 DIN KEG""
     }
     ],
    ""invoiceNumber"": ""C6188372""
}";

StringBuilder sb = new StringBuilder();
using (var p = ChoJSONReader.LoadText(json))
{
    using (var w = new ChoCSVWriter(sb)
        .WithFirstLineHeader()
        )
        w.Write(p
            .SelectMany(r1 => ((dynamic[])r1.lineItems).Select(r2 => new
            {
                r1.contactName,
                r2.quantity,
                r2.description,
                r1.invoiceNumber
            })));
}
Console.WriteLine(sb.ToString());
输出CSV:

contactName,quantity,description,invoiceNumber
Company,7,Beer No* 45.5 DIN KEG,C6188372
Company,2,Beer Old 49.5 DIN KEG,C6188372

希望能有所帮助。

只是想澄清一下,您是否正在使用第一个JSON结构,并希望将其重新构造为第二个结构?或者您的C#类生成第一个JSON结构,而您希望它生成第二个JSON结构(实际上第一个JSON结构不应该存在)@Skintkingle First是正确的-我正在使用第一个JSON结构,我需要将其重新构造为第二个结构。目前是否有一个C#类可以很好地反序列化第一个示例?如果是,你能在问题中提供该类信息吗@Skintkingle由于动态数据结构,我正在处理object/JObject/JToken等,所以(遗憾的是)我没有固定的c#classes,所以您正在读取的JSON有一个未知的数据结构?或者它只是有条件地有时不在这里或那里提供参数?不幸的是,在这种情况下,我并不生活在强类型世界中-输入数据结构由外部Json模式“指定”,可以在任何给定时间修改,我需要适应它:(在这种情况下,我希望包含您的行项目列表的属性不会更改!DavidGs答案可能就是您想要的答案。我刚刚在他的答案下面写了一条评论:我将扩展他的答案,使其更加通用,(以检测数组),这样我就不需要硬编码“行项目”代码中的字符串。谢谢你的帮助。我已经尝试过了,我认为这会奏效。我需要将其扩展为递归,并检测哪些属性是数组,这样我就不需要显式地编写“lineItems”在代码中。无论如何,我已经接受了你的答案,因为它给了我如何继续的想法。谢谢。同时,我已经用DavidGs的答案做了我需要的,但是谢谢你提供的信息!我不知道这个库。我会查查看的。
        var invoice = JsonConvert.DeserializeObject<Invoice>(inputJson);
        var outputJson = JsonConvert.SerializeObject(invoice, new InvoiceFlattener());
string json = @"{
    ""contactName"": ""Company"",
    ""lineItems"": [
     {
        ""quantity"": 7.0,
        ""description"": ""Beer No* 45.5 DIN KEG""
     },
     {
        ""quantity"": 2.0,
        ""description"": ""Beer Old 49.5 DIN KEG""
     }
     ],
    ""invoiceNumber"": ""C6188372""
}";

StringBuilder sb = new StringBuilder();
using (var p = ChoJSONReader.LoadText(json))
{
    using (var w = new ChoCSVWriter(sb)
        .WithFirstLineHeader()
        )
        w.Write(p
            .SelectMany(r1 => ((dynamic[])r1.lineItems).Select(r2 => new
            {
                r1.contactName,
                r2.quantity,
                r2.description,
                r1.invoiceNumber
            })));
}
Console.WriteLine(sb.ToString());
contactName,quantity,description,invoiceNumber
Company,7,Beer No* 45.5 DIN KEG,C6188372
Company,2,Beer Old 49.5 DIN KEG,C6188372