Привет, Хабр!

Сегодня поговорим о том, как сериализовать данные в C# с помощью библиотеки System.Text.Json. Если вы раньше использовали Newtonsoft.Json для преобразования объектов в JSON и обратно, то пришло время посмотреть, как этот процесс можно ускорить и упростить, применяя встроенные инструменты .NET.

System.Text.Json

Сначала взглянем на самый простой сценарий — сериализация объектов в JSON и обратное преобразование. System.Text.Json имеет много методов для работы с JSON, и основные из них — это JsonSerializer.Serialize() и JsonSerializer.Deserialize():

using System.Text.Json;

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeId { get; set; }
}

var employee = new Employee 
{
    FirstName = "Vasya",
    LastName = "Pupkin",
    EmployeeId = 1234
};

// сериализация объекта Employee в строку JSON
string json = JsonSerializer.Serialize(employee);
Console.WriteLine(json);  // {"FirstName":"Vasya","LastName":"Pupkin","EmployeeId":1234}

// десериализация JSON обратно в объект Employee
Employee deserializedEmployee = JsonSerializer.Deserialize<Employee>(json);
Console.WriteLine(deserializedEmployee.FirstName);  // Vasya

Всё просто: создаешь объект, сериализуешь его в строку JSON и обратно.

Вложенные объекты

public class Address
{
    public string City { get; set; }
    public string Country { get; set; }
}

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int EmployeeId { get; set; }
    public Address EmployeeAddress { get; set; }
}

var employee = new Employee
{
    FirstName = "Nastya",
    LastName = "Orlova",
    EmployeeId = 1234,
    EmployeeAddress = new Address { City = "New York", Country = "USA" }
};

string json = JsonSerializer.Serialize(employee);
Console.WriteLine(json);  
// {"FirstName":"Nastya","LastName":"Orlova","EmployeeId":1234,"EmployeeAddress":{"City":"New York","Country":"USA"}}

Employee deserializedEmployee = JsonSerializer.Deserialize<Employee>(json);
Console.WriteLine(deserializedEmployee.EmployeeAddress.City);  // New York

JSON работает с вложенными объектами так же, как и с простыми, сериализация происходит рекурсивно.

System.Text.Json позволяет настроить процесс сериализации через JsonSerializerOptions. Например, можно управлять форматированием JSON или игнорировать значения null.

Пример:

var options = new JsonSerializerOptions
{
    WriteIndented = true,  // Включаем красивое форматирование
    DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull  // Игнорируем null значения
};

var employee = new Employee
{
    FirstName = "Sonya",
    LastName = null,  // Это поле будет проигнорировано
    EmployeeId = 1234
};

string json = JsonSerializer.Serialize(employee, options);
Console.WriteLine(json);
// {
//   "FirstName": "Sonya",
//   "EmployeeId": 1234
// }

Опция WriteIndented хороша, когда нужно сделать JSON более читаемым, например, для логирования. А вот DefaultIgnoreCondition позволяет игнорировать пустые или null поля — удобно для экономии места в передаваемых данных.

Еще можно обрабатывать данные, которые не поддерживаются по умолчанию. Например, сложные объекты или специфические форматы. Для этого нужно создать кастомный конвертер с помощью JsonConverter.

Пример кастомного конвертера для DateTime:

public class CustomDateTimeConverter : JsonConverter<DateTime>
{
    private const string Format = "yyyy-MM-dd";

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string dateString = reader.GetString();
        return DateTime.ParseExact(dateString, Format, null);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(Format));
    }
}

var options = new JsonSerializerOptions
{
    Converters = { new CustomDateTimeConverter() },
    WriteIndented = true
};

DateTime now = DateTime.Now;
string json = JsonSerializer.Serialize(now, options);
Console.WriteLine(json);  // "2024-09-07"

Здесь переопределяем методы Read и Write, чтобы работать с датами в специфичном формате.

Асинхронная сериализация

Когда работаем с потоками или большими объемами данных, асинхронные операции помогают избежать блокировок:

using (FileStream fs = new FileStream("data.json", FileMode.Create))
{
    var employee = new Employee { FirstName = "Alex", LastName = "Champion", EmployeeId = 1234 };
    await JsonSerializer.SerializeAsync(fs, employee);
}

Сериализация в байты

Если нужно максимизировать производительность, можно сериализовать объект в массив байт, что ускоряет передачу данных:

byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(employee);

Это на 5-10% быстрее, чем обычная сериализация в строку.

JsonDocument

Если нужно читать или манипулировать большими JSON-структурами, которые сложно или нежелательно сериализовать в объект, то в помощь идет JsonDocument:

string jsonString = "{\"FirstName\":\"Artem\",\"LastName\":\"Artemovich\",\"EmployeeId\":1234}";
using (JsonDocument doc = JsonDocument.Parse(jsonString))
{
    JsonElement root = doc.RootElement;
    string firstName = root.GetProperty("FirstName").GetString();
    Console.WriteLine(firstName); 
}

Этот подход позволяет работать с JSON на уровне элементов, не создавая для этого целые объекты.

Итог: System.Text.Json — это мощный инструмент для работы с JSON в C#. Он позволяет легко сериализовать и десериализовать объекты, и к тому же настраивать процесс, используя различные опции и кастомные конвертеры.


Как эффективно использовать асинхронность в C# для улучшения производительности приложений? Обсудим это на открытом уроке 12 сентября. Какие темы успеем затронуть:

  • Основы асинхронности в C#: рассмотрим ключевые концепции и принципы асинхронного программирования.

  • Async и Await: подробно разберем ключевые слова async и await, их использование и влияние на код.

  • Лучшие практики: Советы и рекомендации по оптимизации и улучшению асинхронного кода.

Записаться на урок можно на странице курса "C# Developer. Professional".

Комментарии (5)


  1. denchik09
    11.09.2024 13:36

    а чем оно принципиально отличается от JavaScriptSerializer? да, есть асинхронность, но если ваш json такой жирный, что его аж асинхронно обрабатывать надо - вы занимаетесь явно не тем.


    1. khoramus
      11.09.2024 13:36

      JavaScriptSerializer очень медленная библиотека относительно современных. Одним из преимуществ System.Text.Json является то, что он на данный момент самый быстрый, жалко только что настройки неадекватно строгие по умолчанию.


      1. Vest
        11.09.2024 13:36

        Простите, а почему «жаль»?


        1. khoramus
          11.09.2024 13:36

          Прихродится писать какие-то конвертеры, фабрики конверторов, выставлять настройки, хотя в старом добром Newtonsoft можно было этого не делать.

          Например, месяц назад парсил данные из Битрикс24. В JSON строковое поле (хотя по факту там числа) не мог десериализовать в long?, когда в строковом была пустая строка, а Newtonsoft нормально случай обрабатывает и null проставляет.


          1. Vest
            11.09.2024 13:36

            Ясно, спасибо