Как превратить строку в нетривиальный объект 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 шт):
Я практически уверен, что эту "нетривиальную" строку сделали вы сами. Просто измените процесс сериализации, чтобы там не было массива. Ну или свяжитесь с теми, кто выдаёт такой 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.
Можно прочитать вот так.
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() }
});