将 JSON 数组值合并到单个 CSV 列中

Posted

技术标签:

【中文标题】将 JSON 数组值合并到单个 CSV 列中【英文标题】:Merge JSON Array values into a single CSV column 【发布时间】:2021-11-09 02:40:09 【问题描述】:

我有一个类似这样的 JSON 文件:


        "id": 2,
        "name": "I.1.A.2",
        "activeFlag": true,
        "recipients": [
            
                "id": 3,
                "identityName": "idenity1",
                "fullName": "FullName1"
            ,
            
                "id": 4,
                "identityName": "identity2",
                "fullName": "FullName2"
            
        ]
    

我需要使用 C# 和 dotnet Core 将其转换为与此类似的 CSV 输出。

"id","name","activeFlag","identityName"
"2","I.1.A.2","true","identity1;identity2"

但是,我只能得到 CSV 输出:

"id","name","activeFlag","recipients_0", "recipients_1"
"2","I.1.A.2","true","identity1","identity2"

这是生成上述输出的代码:

    using (var csv = new ChoCSVWriter(".\\temp\\csvoutput.csv").WithFirstLineHeader()
    )
    
        using (var json = new ChoJSONReader(".\\temp\\tmpjson.json")
        .Configure(c => c.ConvertToFlattenObject(arrayIndexSeparator: ';'))
        .Configure(c => c.ArrayValueSeparator = ';')
        .Configure(c => c.ArrayValueSeparator = ';')
        .WithField("id", jsonPath: "$..id", isArray: false)
        .WithField("recipients", jsonPath: "$..recipients[*]..identityName", isArray: true, fieldName: "recipients")
)
        
            csv.Write(json);
        
    

现在,我正在使用 ChoEtl 库,但对其他选项/建议持开放态度。一直在寻找这个问题的答案,但还没有找到任何答案。抱歉,如果我还没有找到一些解决方案。我确实在这里尝试了类似的解决方案:How to output JSON array as a single field in CSV using ChoETL 但并不能完全满足我的需求。

【问题讨论】:

请包含创建当前输出的 C# 代码。 谢谢@JackA。!添加代码sn-p。 【参考方案1】:

就个人而言,我发现“投影”方法比基于配置的方法更易于使用和推理。见https://www.codeproject.com/Articles/1193650/Cinchoo-ETL-Quick-Start-Converting-JSON-to-CSV-Fil

这对我有用:

using (var csv = new ChoCSVWriter(output).WithFirstLineHeader()) 
    using (var json = new ChoJSONReader(input))
    
        csv.Write(json.Select(jsonItem =>
        
            var recipientList = new List<dynamic>(jsonItem.recipients);
            string recipientString = string.Join(';', recipientList.Select(r => r.identityName));
            return new
            
                id = jsonItem.id,
                name = jsonItem.name,
                activeFlag = jsonItem.activeFlag,
                identityName = recipientString
            ;
        ));
    

可能有一种更优雅的方式来构造identityName 的值,但由于这些值是动态的,因此很难直接使用 Linq 而不会遇到 CS1977 错误。

但是,如果您更喜欢基于配置的方法,您可以将valueConverterjsonPath 结合使用,如下所示:

using (var csv = new ChoCSVWriter(output).WithFirstLineHeader())

    using (var json = new ChoJSONReader(input)
        .WithField("id")
        .WithField("name")
        .WithField("activeFlag")
        .WithField("recipients", jsonPath: "$.recipients[*].identityName"
            , valueConverter: o => string.Join(';', ((object[])o).Select(x => x.ToString())))
    )
    
        csv.Write(json);
    

我找不到ConvertToFlattenObject 的任何文档,所以我不确定它应该如何工作。

【讨论】:

谢谢!但是我遇到了这样的错误:'object []'不包含'Select'的定义,并且最好的扩展方法重载'ChoDynamicQueryable.Select(IQueryable,string,params object [])'需要类型'的接收器IQueryable' ,不知道为什么,因为它似乎应该工作。 @MarioP 你需要 include System.Linq 以便它识别 Select 扩展方法。【参考方案2】:

@Jack.A 以上答案涵盖了您的场景以产生预期的输出。

这是另一种方法,在 CSV 写入器中将 UseNestedKeyFormat 设置为 false 以生成预期的 CSV 输出

string json = @"

        ""id"": 2,
        ""name"": ""I.1.A.2"",
        ""activeFlag"": true,
        ""recipients"": [
            
                ""id"": 3,
                ""identityName"": ""idenity1"",
                ""fullName"": ""FullName1""
            ,
            
                ""id"": 4,
                ""identityName"": ""identity2"",
                ""fullName"": ""FullName2""
            
        ]
    ";


StringBuilder csv = new StringBuilder();

using (var r = ChoJSONReader.LoadText(json)
    .WithField("id")
    .WithField("name")
    .WithField("activeFlag")
    .WithField("recipients", jsonPath: "$..recipients[*]..identityName")
    )

    using (var w = new ChoCSVWriter(csv)
        .Configure(c => c.ArrayValueSeparator = ';')
        .WithFirstLineHeader()
        .QuoteAllFields()
        .UseNestedKeyFormat(false)
        )
    
        w.Write(r);
    

Console.WriteLine(csv.ToString());

输出:

id,name,activeFlag,recipients
"2","I.1.A.2","True","idenity1,identity2"

小提琴样例:https://dotnetfiddle.net/O1fKQA

【讨论】:

谢谢!差不多了。如何更改“收件人”列的分隔符,因为我需要它是分号。否则,这将完全按预期工作! 回答了我自己的问题 :) 小编辑添加 .Configure(c => c.ArrayValueSeparator = ';') 再次感谢您的快速回复!

以上是关于将 JSON 数组值合并到单个 CSV 列中的主要内容,如果未能解决你的问题,请参考以下文章

如何将列中的所有数据移动到单个列(不合并),然后拆分为R中的新列?

将嵌套的键/值和嵌套列表合并到 json

迭代多个数据帧的合并

如何将Python Dask Dataframes合并到列中?

如何将日期,年,月的不同列合并/合并到单个列中

将具有不同架构(列)的多个文件 (.csv) 合并/合并为单个文件 .csv - Azure 数据工厂