Avatar ·

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

Всем привет! Я новичок в C# и работаю с JSON-данными от внешнего API. Структура JSON может меняться, и я не хочу создавать для каждого случая отдельный класс. Например, у меня есть такой JSON: ```json { "name": "John", "age": 30, "address": { "city": "Moscow", "street": "Tverskaya" } } ``` Но иногда приходит другой JSON: ```json { "product": "Laptop", "price": 1200, "features": ["SSD", "16GB RAM"] } ``` Я пробовал использовать `JavaScriptSerializer` и `JsonConvert.DeserializeObject`, но они требуют заранее определённый класс. Например: ```csharp var result = JsonConvert.DeserializeObject(jsonString); ``` Но если я не знаю структуры заранее, как мне работать с такими данными? Слышал про `dynamic` в C#, но не понимаю, как применить его к JSON. Подскажите, пожалуйста, как десериализовать JSON в динамический объект, чтобы можно было обращаться к полям через точку, например: `obj.name` или `obj.product`? И как это сделать с помощью Newtonsoft.Json или System.Text.Json? Заранее спасибо! <<>>

Использование Newtonsoft.Json (Json.NET)

Самый популярный способ — использовать Newtonsoft.Json. Он поддерживает динамические объекты через тип JObject или напрямую через dynamic с помощью метода JsonConvert.DeserializeObject.

Способ 1: Через dynamic

Просто укажите dynamic как целевой тип:

using Newtonsoft.Json;

string json = @"{""name"": ""John"", ""age"": 30}";
dynamic obj = JsonConvert.DeserializeObject(json);
Console.WriteLine(obj.name); // John
Console.WriteLine(obj.age);  // 30

Этот метод автоматически создаёт JObject или JArray в зависимости от JSON. Вы можете обращаться к свойствам через точку, как к обычным объектам.

Способ 2: Через JObject

Если нужен более строгий контроль, используйте JObject.Parse:

using Newtonsoft.Json.Linq;

string json = @"{""name"": ""John"", ""age"": 30}";
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["name"]);  // John
Console.WriteLine(obj["age"]);   // 30

JObject позволяет работать с вложенными структурами:

string json = @"{""address"": {""city"": ""Moscow""}}";
JObject obj = JObject.Parse(json);
Console.WriteLine(obj["address"]["city"]); // Moscow

Обработка массивов

Для массивов используйте JArray:

string json = @"{""features"": [""SSD"", ""16GB RAM""]}";
dynamic obj = JsonConvert.DeserializeObject(json);
foreach (var feature in obj.features)
{
    Console.WriteLine(feature);
}

Использование System.Text.Json (.NET Core 3.0+)

Встроенная библиотека System.Text.Json не поддерживает dynamic напрямую, но можно использовать JsonDocument для гибкого доступа:

using System.Text.Json;

string json = @"{""name"": ""John"", ""age"": 30}";
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
Console.WriteLine(root.GetProperty("name").GetString()); // John
Console.WriteLine(root.GetProperty("age").GetInt32());   // 30

Для вложенных объектов:

string json = @"{""address"": {""city"": ""Moscow""}}";
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement address = doc.RootElement.GetProperty("address");
Console.WriteLine(address.GetProperty("city").GetString()); // Moscow

Рекомендации

  • Newtonsoft.Json — лучший выбор для dynamic и простоты. Установите через NuGet: Install-Package Newtonsoft.Json.
  • System.Text.Json — быстрее и без внешних зависимостей, но требует больше кода для динамического доступа.
  • Если структура JSON известна хотя бы частично, рассмотрите создание классов с JsonExtensionData для неизвестных полей.

Пример с ExpandoObject (альтернатива):

using Newtonsoft.Json;
using System.Dynamic;

string json = @"{""name"": ""John""}";
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json);
Console.WriteLine(obj.name);

Этот метод даёт полностью динамический объект, к которому можно добавлять свойства на лету.

<<>>

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

Если вы не хотите подключать Newtonsoft.Json или работать с dynamic, можно десериализовать JSON в Dictionary<string, object> и обращаться к полям по ключу. Это даёт полный контроль без неявных преобразований.

Пример с System.Text.Json

using System.Text.Json;

string json = @"{""name"": ""John"", ""age"": 30}";
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
Console.WriteLine(dict["name"]); // John

Для вложенных объектов нужно рекурсивно обрабатывать JsonElement:

string json = @"{""address"": {""city"": ""Moscow""}}";
var doc = JsonDocument.Parse(json);
var dict = new Dictionary<string, object>();
foreach (var prop in doc.RootElement.EnumerateObject())
{
    dict[prop.Name] = prop.Value;
}
Console.WriteLine(dict["address"]); // { "city": "Moscow" }

Плюсы и минусы

  • Плюсы: Безопасность типов (нет риска опечаток в именах свойств), поддержка всех версий .NET.
  • Минусы: Больше кода, нет синтаксиса через точку, сложнее работать с массивами.

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