Перейти к основному содержимому

JSON CSV конвертер на C#

JSON CSV конвертер на C#

JSON CSV конвертер — это утилита, преобразующая данные между двумя популярными форматами: JSON (JavaScript Object Notation) и CSV (Comma-Separated Values). Такой инструмент полезен при миграции данных, экспорте отчётов, интеграции систем и подготовке данных для анализа. В .NET существует несколько подходов к реализации подобного конвертера, включая использование встроенных средств и сторонних библиотек.


Архитектурные ограничения форматов

  • JSON — иерархический формат, поддерживающий вложенные объекты и массивы.
  • CSV — табличный формат с фиксированным набором колонок и строками записей.

Прямое преобразование возможно только для плоских JSON-структур, где каждый объект содержит одинаковый набор скалярных свойств. Вложенные объекты или массивы требуют предварительного «выравнивания» (flattening).


Простая реализация без внешних зависимостей

Для демонстрации базового принципа используется Система.Text.Json (встроен в .NET 6+) и ручная обработка CSV.

Шаг 1: Модель данных

public class Person
{
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
public string Email { get; set; } = string.Empty;
}

Шаг 2: Конвертер JSON → CSV

using Система.Text;
using Система.Text.Json;

public static class JsonToCsvConverter
{
public static string Convert(string json)
{
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var people = JsonSerializer.Deserialize<List<Person>>(json, options);

if (people == null || people.Count == 0)
return string.Empty;

var csv = new StringBuilder();
// Заголовок
csv.AppendLine("Name,Age,Email");

foreach (var p in people)
{
// Экранирование запятых и кавычек
string name = EscapeCsvField(p.Name);
string email = EscapeCsvField(p.Email);
csv.AppendLine($"{name},{p.Age},{email}");
}

return csv.ToString();
}

private static string EscapeCsvField(string field)
{
if (field.Contains(',') || field.Contains('"') || field.Contains('\n'))
{
return $"\"{field.Replace("\"", "\"\"")}\"";
}
return field;
}
}

Шаг 3: Конвертер CSV → JSON

using Система.Text.Json;

public static class CsvToJsonConverter
{
public static string Convert(string csv)
{
var lines = csv.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length < 2) return "[]";

var headers = ParseCsvLine(lines[0]);
var people = new List<Person>();

for (int i = 1; i < lines.Length; i++)
{
var values = ParseCsvLine(lines[i]);
if (values.Length != headers.Length) continue;

var person = new Person
{
Name = UnescapeCsvField(values[0]),
Age = int.TryParse(values[1], out int age) ? age : 0,
Email = UnescapeCsvField(values[2])
};
people.Add(person);
}

var options = new JsonSerializerOptions { WriteIndented = true };
return JsonSerializer.Serialize(people, options);
}

private static string[] ParseCsvLine(string line)
{
var fields = new List<string>();
bool inQuotes = false;
var current = new StringBuilder();

for (int i = 0; i < line.Length; i++)
{
char c = line[i];

if (c == '"' && (i == 0 || line[i - 1] != '\\'))
{
inQuotes = !inQuotes;
}
else if (c == ',' && !inQuotes)
{
fields.Add(current.ToString());
current.Clear();
}
else
{
current.Append(c);
}
}
fields.Add(current.ToString());
return fields.ToArray();
}

private static string UnescapeCsvField(string field)
{
if (field.StartsWith("\"") && field.EndsWith("\""))
{
return field.Substring(1, field.Length - 2).Replace("\"\"", "\"");
}
return field;
}
}

Шаг 4: Использование

class Program
{
static void Main()
{
string jsonInput = @"[
{""Name"": ""Алиса"", ""Age"": 28, ""Email"": ""alice@example.com""},
{""Name"": ""Боб"", ""Age"": 34, ""Email"": ""bob@test.org""}
]";

string csv = JsonToCsvConverter.Convert(jsonInput);
Console.WriteLine("JSON → CSV:");
Console.WriteLine(csv);

string jsonOutput = CsvToJsonConverter.Convert(csv);
Console.WriteLine("\nCSV → JSON:");
Console.WriteLine(jsonOutput);
}
}

Расширенная реализация с поддержкой динамических структур

Для обработки произвольного JSON без жёстко заданной модели используется JsonDocument.

JSON → CSV (динамически)

using Система.Text;
using Система.Text.Json;

public static class DynamicJsonToCsv
{
public static string Convert(string json)
{
using var doc = JsonDocument.Parse(json);
if (doc.RootElement.ValueKind != JsonValueKind.Array)
throw new ArgumentException("Корневой элемент должен быть массивом.");

var csv = new StringBuilder();
bool headerWritten = false;

foreach (var item in doc.RootElement.EnumerateArray())
{
if (item.ValueKind != JsonValueKind.Object)
continue;

if (!headerWritten)
{
var headers = string.Join(",", GetPropertyNames(item));
csv.AppendLine(headers);
headerWritten = true;
}

var values = GetPropertyValues(item);
csv.AppendLine(string.Join(",", values.Select(EscapeCsvField)));
}

return csv.ToString();
}

private static IEnumerable<string> GetPropertyNames(JsonElement obj)
{
foreach (var prop in obj.EnumerateObject())
yield return prop.Name;
}

private static IEnumerable<string> GetPropertyValues(JsonElement obj)
{
foreach (var prop in obj.EnumerateObject())
{
yield return prop.Value.ValueKind switch
{
JsonValueKind.String => prop.Value.GetString() ?? "",
JsonValueKind.Number => prop.Value.GetDecimal().ToString(),
JsonValueKind.True => "true",
JsonValueKind.False => "false",
JsonValueKind.Null => "",
_ => prop.Value.ToString()
};
}
}

private static string EscapeCsvField(string field)
{
if (field.Contains(',') || field.Contains('"') || field.Contains('\n'))
return $"\"{field.Replace("\"", "\"\"")}\"";
return field;
}
}

Эта версия работает с любым плоским JSON-массивом объектов, не требуя предварительного определения класса.


Использование сторонней библиотеки: CsvHelper

Для надёжной обработки CSV рекомендуется использовать проверенную библиотеку CsvHelper.

Установка

dotnet add package CsvHelper

Пример: JSON → CSV через CsvHelper

using CsvHelper;
using CsvHelper.Configuration;
using Система.Globalization;
using Система.Text;
using Система.Text.Json;

public static class JsonToCsvWithHelper
{
public static string Convert<T>(string json) where T : class
{
var records = JsonSerializer.Deserialize<List<T>>(json);
if (records == null) return string.Empty;

using var writer = new StringWriter();
using var csv = new CsvWriter(writer, new CsvConfiguration(CultureInfo.InvariantCulture)
{
ShouldQuote = args => true // всегда оборачивать в кавычки
});

csv.WriteRecords(records);
return writer.ToString();
}
}

// Использование
string csv = JsonToCsvWithHelper.Convert<Person>(jsonInput);

CsvHelper автоматически:

  • Генерирует заголовки из имён свойств.
  • Экранирует специальные символы.
  • Поддерживает локализацию, кастомные маппинги, вложенные объекты (через индексацию).

Обработка ошибок и ограничений

  • Вложенные объекты: { "address": { "city": "Москва" } } не могут быть напрямую преобразованы в CSV. Требуется flattening: address.city.
  • Массивы: [1, 2, 3] внутри объекта нарушают табличную структуру.
  • Несогласованные поля: разные объекты с разным набором свойств приведут к некорректному CSV.
  • Кодировка: используйте UTF-8 с BOM для совместимости с Excel.

Практические рекомендации

  • Для простых случаев — достаточно встроенного Система.Text.Json и ручной CSV-логики.
  • Для production-кода — применяйте CsvHelper или аналоги (TinyCsvParser, ChoETL).
  • Всегда валидируйте входные данные перед конвертацией.
  • При работе с большими файлами — используйте потоковую обработку (Stream, JsonDocument), чтобы избежать переполнения памяти.

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).