Как превратить строку в нетривиальный объект C#?

Есть вот такая строка, приходящая в WebSocket:

["price_changed",{"id":"4243953","name":"AWP | Duality (Battle-Scarred)","class_id":"5434880573","skin_id":450,"suggested_price":1850,"asset_id":"38175556581","float_id":"14035657510494034055","paint_seed":917,"tradehold":7,"price":1750,"app_id":730,"old_price":1720,"bot_steam_id":"76561198260669567","float_value":0.46425825357437134}]

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

public class PriceChangedResponce
{
    public string Action { get; set; }
    public PriceChangedItem priceChangedItem { get; set; }
}

public class PriceChangedItem
{
    public string id { get; set; }
    public string name { get; set; }
    public string class_id { get; set; }
    public int skin_id { get; set; }
    public int suggested_price { get; set; }
    public string asset_id { get; set; }
    public object float_id { get; set; }
    public object paint_seed { get; set; }
    public int tradehold { get; set; }
    public int price { get; set; }
    public int app_id { get; set; }
    public int old_price { get; set; }
    public string bot_steam_id { get; set; }
    public object float_value { get; set; }
}

Как превратить эту строку в объект данного класса? Через JsonConvert.DeserializeObject() не получится - конвертер воспринимает строку как массив, но для массива содержимое получается слишком разнорордное и пасить строку, как object[] а потом приводить каждый элемент своеобразного "массива" к классу мне кажется непрактичным решением.

Замена внешних скобок в строке и последующая десериализация как Json тоже не совсем подходит - читаю непрерывно данные из WebSocket и большое кол-во таких операций скажется на производительности приложения.

Есть ли более простое \ лаконичное \ каноничное для таких кейсов, решение?


Ответы (2 шт):

Автор решения: Alexander Petrov

Я практически уверен, что эту "нетривиальную" строку сделали вы сами. Просто измените процесс сериализации, чтобы там не было массива. Ну или свяжитесь с теми, кто выдаёт такой JSON, пусть исправят.


А так, всё просто:

using System.Text.Json;

var json = File.ReadAllText("test.json");
var array = JsonSerializer.Deserialize<object[]>(json);

var action = array[0].ToString();
var item = JsonSerializer.Deserialize<PriceChangedItem>(array[1].ToString());

var response = new PriceChangedResponce
{
    Action = action,
    priceChangedItem = item
};

Да, это непрактично. Поэтому нужно исправлять генерацию исходного JSON.

→ Ссылка
Автор решения: vitidev

Можно прочитать вот так.

public class PriceChangedResponceConverter : JsonConverter
{
    ...

    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
        JsonSerializer serializer)
    {
        var action = reader.Value!.ToString()!;
        reader.Read();
        var item = serializer.Deserialize<PriceChangedItem>(reader)!;
        return new PriceChangedResponce { Action = action, priceChangedItem = item };
    }

    public override bool CanConvert(Type objectType) => typeof(PriceChangedResponce) == objectType;
}

и читаем

 var result = JsonConvert.DeserializeObject<IList<PriceChangedResponce>>(json,
                                                  new PriceChangedResponceConverter());

upd: Вариант для System.Text.Json

А вот для System.Text.Json так просто не работает. Слишком придирается вместо того чтобы просто циклически вызывать конвертер. Приходится писать больше кода на более низком уровне и самому собирать список. Ну хоть для null он сам разберется и не нужно про него в конвертере думать (ура)

public class PriceChangedResponceConverter : JsonConverter<IList<PriceChangedResponce>>
{
    public override IList<PriceChangedResponce>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var items = new List<PriceChangedResponce>();
        while (true)
        {
            reader.Read();
            if(reader.TokenType==JsonTokenType.EndArray)
                break;
            var action = reader.GetString();
            reader.Read();
            var item = JsonSerializer.Deserialize<PriceChangedItem>(ref reader)!;
            items.Add(new PriceChangedResponce { Action = action, priceChangedItem = item });
        }
        return items;
    }

    ...
}

и чтение

 var result =
     JsonSerializer.Deserialize<IList<PriceChangedResponce>>(json, new JsonSerializerOptions()
     {
         Converters = { new PriceChangedResponceConverter() }
     });
→ Ссылка