Конвертируйте вложенный JSON в CSV на C # с помощью ChoETL

#c# #json #csv #choetl

#c# #json #csv #choetl

Вопрос:

Кто-нибудь знает, как преобразовать приведенный ниже вложенный JSON в CSV с помощью CHOETL (платформа ETL для .NET)? Спасибо!

Я использую этот код, но он вернет только первую запись оборудования.

код:

                  {
                     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"
            }
        ]
    }
}````
  

Комментарии:

1. Вы делаете что-то только с i.GpsLocation. Оборудование [0], которое действительно является первой записью оборудования. Я подозреваю, что вы хотите перебрать все записи.

Ответ №1:

Как предполагают другие, проблема в том, что вы просматриваете только первый элемент массива.

Похоже, что самый простой способ контролировать то, что вы сериализуете в CSV, — это правильно определять ваши исходные объекты из JSON. Выражения пути в формате JSON очень удобны.

В итоге я сделал запрос ко всему 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 — см., например, обновленный код

Комментарии:

1. Потрясающе! Большое вам спасибо. И еще одна вещь: код работает отлично, но он не включает заголовки для каждого столбца (EquipmentId,InquiryValue, Timestamp). Я сохраняю выходные данные в файл CSV, используя приведенный ниже код. string csvOutput = (csvWriter.GetStringBuilder().ToString()); File.WriteAllText("./foo.csv", csvOutput.ToString());

Ответ №2:

Вот рабочий пример создания CSV из вашего JSON

 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

Ответ №3:

Ваш код исправен, но проблема в том, что вы записываете только первую переменную в массиве, используя i.GpsLocation.Оборудование[0]. Вместо этого попробуйте выполнить цикл по всему, поместив его в цикл for и изменив [0] на вашу повторяющуюся переменную внутри указанного цикла.