【问题标题】:Convert Nested JSON to CSV in C# via ChoETL通过 ChoETL 在 C# 中将嵌套的 JSON 转换为 CSV
【发布时间】:2020-12-02 11:06:01
【问题描述】:

有谁知道如何通过 CHOETL(.NET 的 ETL 框架)将下面的嵌套 JSON 转换为 CSV?谢谢!

我正在使用此代码,但它只会返回第一个设备记录。

代码:

                 {
                     using (var json = new ChoJSONReader("./test.json"))
                     {
                         csv.Write(json.Cast<dynamic>().Select(i => new
                         {
                             EquipmentId = i.GpsLocation.Equipment[0].EquipmentId,
                             InquiryValue = i.GpsLocation.Equipment[0].InquiryValue,
                             Timestamp = i.GpsLocation.Equipment[0].Timestamp

                         }));
                     }
                 }

JSON:

    "GpsLocation": {
        "Equipment": [
            {
                "EquipmentId": "EQ00001",
                "InquiryValue": [
                    "IV00001"
                ],
                "Timestamp": "2020-01-01 01:01:01.01",
            },
            {
                "EquipmentId": "EQ00002",
                "InquiryValue": [
                    "IV00002"
                ],
                "Timestamp": "2020-01-01 01:01:01.01"
            }
        ]
    }
}````

【问题讨论】:

  • 你只是在用 i.GpsLocation.Equipment[0] 做一些事情,这确实是第一个 Equipment 记录。我怀疑您想遍历 所有 记录。

标签: c# json csv choetl


【解决方案1】:

这是从 JSON 生成 CSV 的工作示例

string json = @"{
""GpsLocation"": {
        ""Equipment"": [
            {
                ""EquipmentId"": ""EQ00001"",
                ""InquiryValue"": [
                    ""IV00001""
                ],
                ""Timestamp"": ""2020-02-01 01:01:01.01"",
            },
            {
                ""EquipmentId"": ""EQ00002"",
                ""InquiryValue"": [
                    ""IV00002""
                ],
                ""Timestamp"": ""2020-01-01 01:01:01.01""
            }
        ]
    }
}";

StringBuilder csv = new StringBuilder();

using (var r = ChoJSONReader.LoadText(json)
    .WithJSONPath("$.GpsLocation.Equipment")
    .WithField("EquipmentId")
    .WithField("InquiryValue", jsonPath: "InquiryValue[0]", fieldType: typeof(string))
    .WithField("Timestamp", fieldType: typeof(DateTime))
    )
{
    using (var w = new ChoCSVWriter(csv)
        .WithFirstLineHeader())
        w.Write(r);
}
Console.WriteLine(csv.ToString());

输出:

EquipmentId,InquiryValue,Timestamp
EQ00001,IV00001,2/1/2020 1:01:01 AM
EQ00002,IV00002,1/1/2020 1:01:01 AM

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

【讨论】:

    【解决方案2】:

    正如其他人所建议的,问题是您只查看数组的第一个元素。

    看来,控制序列化为 CSV 的最简单方法是从 JSON 中正确定义源对象。 JSON Path expressions 派上用场了。

    我在这里最终做的是查询所有 JSON 以返回 Equipment 对象数组无论它们在层次结构中的什么位置(这意味着您可能需要更好地过滤它,具体取决于在您的完整 JSON 上)。 然后很容易根据 JSON 路径定义每个字段并将结果传递给CSVWriter

    还可以查看我在相应评论行中概述的一些问题。

    void Main()
    {
        var jsonString = "{\"GpsLocation\":{\"Equipment\":[{\"EquipmentId\":\"EQ00001\",\"InquiryValue\":[\"IV00001\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"},{\"EquipmentId\":\"EQ00002\",\"InquiryValue\":[\"IV00002\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"}]}}";
        var jsonReader = new StringReader(jsonString);
        var csvWriter = new StringWriter(); // outputs to string, comment out if you want file output
        //var csvWriter = new StreamWriter(".\\your_output.csv"); // writes to a file of your choice
        using (var csv = new ChoCSVWriter(csvWriter))
        using (var json = new ChoJSONReader(jsonReader)
                            .WithJSONPath("$..Equipment[*]", true) // firstly you scope the reader to all Equipment objects. take note of the second parameter. Apparently you need to pass true here as otherwise it just won't return anythig
                            .WithField("EquipmentId", jsonPath: "$.EquipmentId", isArray: false) // then you scope each field in the array to what you want it to be. Since you want scalar values, pass `isArray: false` for better predictability
                            .WithField("InquiryValue", jsonPath: "$.InquiryValue[0]", isArray: false) // since your InquiryValue is actually an array, you want to obtain first element here. if you don't do this, fields names and values would go askew
                            .WithField("Timestamp", jsonPath: "$.Timestamp", fieldType: typeof(DateTime), isArray: false)) // you can also supply field type, otherwise it seems to default to `string`
        {   
            csv.WithFirstLineHeader().Write(json);
        }
        Console.WriteLine(csvWriter.GetStringBuilder().ToString()); // comment this out if writing to file - you won't need it
    }
    

    更新摘要:

    1. 转而更新代码以依赖 JSON 路径范围 - 这似乎允许以非常低的工作量进行字段名称操作
    2. 查看您的评论,您可能会稍微简化文件编写器 - 使用 StreamWriter 而不是 StringWriter - 请参阅更新的代码示例

    【讨论】:

    • 太棒了!非常感谢。但还有一件事,代码效果很好,但它不包括每一列的标题(EquipmentId、InquiryValue、Timestamp)。我正在使用下面的代码将输出保存到 CSV 文件。 string csvOutput = (csvWriter.GetStringBuilder().ToString()); File.WriteAllText("./foo.csv", csvOutput.ToString());
    【解决方案3】:

    您的代码是正确的,但问题是您只使用 i.GpsLocation.Equipment[0] 写入数组中的第一个变量。相反,尝试通过将其放入 for 循环来循环所有内容,并将 [0] 更改为所述循环内的迭代变量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-07
      • 2020-10-28
      • 2017-10-25
      • 1970-01-01
      相关资源
      最近更新 更多