Avatar ·

Как десериализовать JSON в dynamic объект в C# без создания классов?

Всем привет! Я новичок в C# и работаю с JSON-данными от внешнего API. Проблема в том, что структура JSON постоянно меняется, и мне приходится каждый раз создавать новые классы для десериализации. Это очень неудобно, особенно когда нужно быстро протестировать ответ сервера. Вот пример JSON, который я получаю: ```json { "name": "John Doe", "age": 30, "address": { "city": "New York", "zip": "10001" }, "hobbies": ["reading", "gaming"] } ``` Сейчас я делаю так: ```csharp public class Person { public string Name { get; set; } public int Age { get; set; } public Address Address { get; set; } public List Hobbies { get; set; } } public class Address { public string City { get; set; } public string Zip { get; set; } } var json = @"..."; // мой JSON var person = JsonConvert.DeserializeObject(json); Console.WriteLine(person.Name); ``` Но каждый раз писать классы — это боль. Я слышал, что в C# есть dynamic и можно как-то десериализовать JSON без строгой типизации. Как это сделать? Можно ли использовать Newtonsoft.Json или System.Text.Json для этого? Буду благодарен за примеры кода. <<>>

Использование Newtonsoft.Json с dynamic

Да, вы абсолютно правы — в C# можно десериализовать JSON в dynamic объект, и это отличное решение для случаев, когда структура данных нестабильна или вы просто хотите быстро поработать с ответом API. Самый популярный способ — использовать библиотеку Newtonsoft.Json (она же Json.NET).

Вот как это делается:

using Newtonsoft.Json;
using System;

string json = @"{
    ""name"": ""John Doe"",
    ""age"": 30,
    ""address"": {
        ""city"": ""New York"",
        ""zip"": ""10001""
    },
    ""hobbies"": [""reading"", ""gaming""]
}";

dynamic person = JsonConvert.DeserializeObject(json);

Console.WriteLine(person.name);        // John Doe
Console.WriteLine(person.age);         // 30
Console.WriteLine(person.address.city); // New York
Console.WriteLine(person.hobbies[0]);  // reading

Главный трюк здесь — не указывать тип в угловых скобках метода DeserializeObject. Если вызвать JsonConvert.DeserializeObject(json) без generic-параметра, метод вернёт JObject, который неявно преобразуется в dynamic. Это позволяет обращаться к свойствам как к полям обычного объекта.

Важные моменты и ограничения:

  • Регистрозависимость: По умолчанию Newtonsoft.Json чувствителен к регистру. Если ваш JSON использует PascalCase (например, "Name"), то и обращаться нужно через person.Name.
  • Вложенные объекты: Работают отлично — можно цепочкой обращаться к любым уровням вложенности.
  • Массивы: Доступны по индексу, но будьте осторожны — если индекс выходит за границы, вы получите исключение во время выполнения.
  • Отсутствующие свойства: Если попытаться обратиться к свойству, которого нет в JSON, вы получите RuntimeBinderException. Используйте ContainsKey или проверку через JObject для безопасности.

Пример с проверкой существования свойства:

dynamic person = JsonConvert.DeserializeObject(json);

// Безопасный способ
if (((IDictionary<string, JToken>)person).ContainsKey("email"))
{
    Console.WriteLine(person.email);
}
else
{
    Console.WriteLine("Свойство email отсутствует");
}

Работа с System.Text.Json (встроенный в .NET Core 3+):

Начиная с .NET Core 3.0, в стандартной библиотеке есть System.Text.Json. С ним тоже можно работать с dynamic, но немного сложнее — нужно использовать JsonDocument или JsonNode:

using System.Text.Json;
using System.Text.Json.Nodes;

string json = @"{
    ""name"": ""John Doe"",
    ""age"": 30
}";

JsonNode node = JsonNode.Parse(json);
string name = node["name"].GetValue<string>();
int age = node["age"].GetValue<int>();

Console.WriteLine($"{name}, {age}");

Однако JsonNode не является полноценным dynamic — это строго типизированная модель. Если вам нужна именно динамическая работа, Newtonsoft.Json остаётся лучшим выбором благодаря своей гибкости.

Заключение

Используйте JsonConvert.DeserializeObject(json) без указания типа — это самый простой и быстрый способ получить dynamic объект. Но помните о безопасности: проверяйте наличие свойств и типы данных, чтобы избежать ошибок времени выполнения. Для production-кода, где структура JSON стабильна, лучше всё-таки создавать классы — это даёт проверку типов на этапе компиляции и лучшую производительность.

<<>>

Альтернативный подход: использование ExpandoObject

Если вы хотите работать с dynamic, но при этом иметь больше контроля над структурой, можно использовать ExpandoObject из пространства имён System.Dynamic. Он позволяет динамически добавлять и удалять свойства во время выполнения.

Вот как это можно применить к вашей задаче с помощью Newtonsoft.Json:

using Newtonsoft.Json;
using System.Dynamic;

string json = @"{
    ""name"": ""John Doe"",
    ""age"": 30,
    ""address"": { ""city"": ""New York"", ""zip"": ""10001"" }
}";

// Десериализуем в ExpandoObject
dynamic person = JsonConvert.DeserializeObject<ExpandoObject>(json);

Console.WriteLine(person.name);         // John Doe
Console.WriteLine(person.address.city); // New York

// Можно динамически добавить новое свойство
person.email = "john@example.com";
Console.WriteLine(person.email);        // john@example.com

Плюсы этого подхода:

  • Свойства можно добавлять и удалять после десериализации.
  • Проще контролировать, какие свойства существуют, используя IDictionary<string, object>.

Минусы:

  • Немного медленнее, чем прямая десериализация в JObject.
  • Требует дополнительного приведения типов для вложенных объектов.

Этот метод удобен, когда вам нужно не только читать JSON, но и модифицировать его структуру на лету. Для простого чтения лучше использовать первый вариант с dynamic.