{
"rates_scores_stats": [{
"name": 10,
"value": 3545
}, {
"name": 9,
"value": 1004
}, {
"name": 8,
"value": 820
}, {
"name": 7,
"value": 493
}, {
"name": 6,
"value": 218
}, {
"name": 5,
"value": 138
}, {
"name": 4,
"value": 80
}, {
"name": 3,
"value": 41
}, {
"name": 2,
"value": 26
}, {
"name": 1,
"value": 83
}
],
"rates_statuses_stats": [{
"name": "Запланировано",
"value": 2506
}, {
"name": "Смотрю",
"value": 7861
}, {
"name": "Просмотрено",
"value": 1947
}, {
"name": "Отложено",
"value": 1443
}, {
"name": "Брошено",
"value": 1358
}
]
}
Есть JSON описанный выше. Для десериализации я использую Newtonsoft.Json. В частности, метод JsonConvert.DeserializeObject(json)
Я знаю, что для этого json можно описать типичный класс следующим образом
Тоже самое текстом: http://pastexen.com/code.php?file=aMvORa6As3.cs
Но мне надо превратить это не в коллекцию вспомогательных классов или же коллекцию KeyValuePair. Мне нужен именно словарь, чтобы я мог обратиться к такому объекту следующим образом:
var result = JsonConvert.DeserializeObject<MyClass>(json);
var val = result.rates_scores_stats[10]; // val = 3545
var val1 = result.rates_statuses_stats["Смотрю"]; // val1 = 7861
Можно ли как-нибудь сериализовать это в словарь? Может с помощью get/set как-то извернуться?
P.S.: Я знаю, что в шарпе есть Linq, который позволит сделать что-то типо result.FirstOrDefault(x.value => x.name == "10"), но я пишу переносимую библиотеку и хочу упростить доступ к данным =/
Например, можно десериализовать в JObject
, и потом сконвертировать данные вручную:
var obj = JObject.Parse(json);
var rates_scores_stats =
((JArray)obj["rates_scores_stats"]).ToDictionary(entry => (int)entry["name"],
entry => (int)entry["value"]);
var rates_statuses_stats =
((JArray)obj["rates_statuses_stats"]).ToDictionary(entry => (string)entry["name"],
entry => (int)entry["value"]);
var result = new MyClass()
{
rates_scores_stats = rates_scores_stats,
rates_statuses_stats = rates_statuses_stats
};
Альтернативный ответ здесь. Немного модифицировав его, можно получить менее «ручное» решение.
Создадим отдельный конвертер (спёрт и модифицирован из ответа по ссылке):
public class JsonGenericDictionaryOrArrayConverter : JsonConverter
{
// поскольку у вас в JSON'е не key/value, а name/value, нужен специальный класс
// для десериализации одного элемента массива
class NameValuePair<N, V>
{
public N name { get; set; }
public V value { get; set; }
}
public override bool CanConvert(Type objectType)
{
return GetDictionaryKeyValueTypes(objectType).Count() == 1;
}
public override bool CanWrite { get { return false; } }
object ReadJsonGeneric<TKey, TValue>(
JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var tokenType = reader.TokenType;
var dict = existingValue as IDictionary<TKey, TValue>;
if (dict == null)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
dict = (IDictionary<TKey, TValue>)contract.DefaultCreator();
}
if (tokenType == JsonToken.StartArray)
{
var pairs = new JsonSerializer()
.Deserialize<NameValuePair<TKey, TValue>[]>(reader);
if (pairs == null)
return existingValue;
foreach (var pair in pairs)
dict.Add(pair.name, pair.value);
}
else if (tokenType == JsonToken.StartObject)
{
// Using "Populate()" avoids infinite recursion.
// https://github.com/JamesNK/Newtonsoft.Json/blob/
// ee170dc5510bb3ffd35fc1b0d986f34e33c51ab9/Src/Newtonsoft.Json/Converters/
// CustomCreationConverter.cs
serializer.Populate(reader, dict);
}
return dict;
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
// Throws an exception if not exactly one.
var keyValueTypes = GetDictionaryKeyValueTypes(objectType).Single();
var method = GetType().GetMethod(
"ReadJsonGeneric",
BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
var genericMethod = method.MakeGenericMethod(
new[] { keyValueTypes.Key, keyValueTypes.Value });
return genericMethod.Invoke(
this,
new object[] { reader, objectType, existingValue, serializer });
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
static IEnumerable<KeyValuePair<Type, Type>> GetDictionaryKeyValueTypes(Type type)
{
foreach (Type intType in GetInterfacesAndSelf(type))
{
if (intType.IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var args = intType.GetGenericArguments();
if (args.Length == 2)
yield return new KeyValuePair<Type, Type>(args[0], args[1]);
}
}
}
static IEnumerable<Type> GetInterfacesAndSelf(Type type)
{
if (type == null)
throw new ArgumentNullException();
if (type.IsInterface)
return new[] { type }.Concat(type.GetInterfaces());
else
return type.GetInterfaces();
}
}
Имея этот вспомогательный класс, можно десериализовать просто так:
var settings = new JsonSerializerSettings
{
Converters = { new JsonGenericDictionaryOrArrayConverter() }
};
var result = JsonConvert.DeserializeObject<MyClass>(jsonString, settings);
Обновление: Для portable library (.NET 4.5.1 + Windows Universal 8.1 + Windows Phone 8.1) у меня скомпилировалось такое:
public class JsonGenericDictionaryOrArrayConverter : JsonConverter
{
// поскольку у вас в JSON'е не key/value, а name/value, нужен специальный класс
// для десериализации одного элемента массива
class NameValuePair<N, V>
{
public N name { get; set; }
public V value { get; set; }
}
public override bool CanConvert(Type objectType)
{
return GetDictionaryKeyValueTypes(objectType).Count() == 1;
}
public override bool CanWrite { get { return false; } }
public object ReadJsonGeneric<TKey, TValue>(
JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var tokenType = reader.TokenType;
var dict = existingValue as IDictionary<TKey, TValue>;
if (dict == null)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
dict = (IDictionary<TKey, TValue>)contract.DefaultCreator();
}
if (tokenType == JsonToken.StartArray)
{
var pairs = new JsonSerializer()
.Deserialize<NameValuePair<TKey, TValue>[]>(reader);
if (pairs == null)
return existingValue;
foreach (var pair in pairs)
dict.Add(pair.name, pair.value);
}
else if (tokenType == JsonToken.StartObject)
{
// Using "Populate()" avoids infinite recursion.
// https://github.com/JamesNK/Newtonsoft.Json/blob/
// ee170dc5510bb3ffd35fc1b0d986f34e33c51ab9/Src/Newtonsoft.Json/Converters/
// CustomCreationConverter.cs
serializer.Populate(reader, dict);
}
return dict;
}
public override object ReadJson(
JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
// Throws an exception if not exactly one.
var keyValueTypes = GetDictionaryKeyValueTypes(objectType).Single();
var method = GetType().GetTypeInfo().GetDeclaredMethod("ReadJsonGeneric");
var genericMethod = method.MakeGenericMethod(
new[] { keyValueTypes.Key, keyValueTypes.Value });
return genericMethod.Invoke(
this,
new object[] { reader, objectType, existingValue, serializer });
}
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
static IEnumerable<KeyValuePair<Type, Type>> GetDictionaryKeyValueTypes(Type type)
{
foreach (Type intType in GetInterfacesAndSelf(type))
{
if (intType.GetTypeInfo().IsGenericType
&& intType.GetGenericTypeDefinition() == typeof(IDictionary<,>))
{
var args = intType.GetTypeInfo().GenericTypeArguments;
// вроде бы именно GenericTypeArguments, а не Parameters
if (args.Length == 2)
yield return new KeyValuePair<Type, Type>(args[0], args[1]);
}
}
}
static IEnumerable<Type> GetInterfacesAndSelf(Type type)
{
if (type == null)
throw new ArgumentNullException();
if (type.GetTypeInfo().IsInterface)
return new[] { type }.Concat(type.GetTypeInfo().ImplementedInterfaces);
else
return type.GetTypeInfo().ImplementedInterfaces;
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Заранее прошу прощения если вопрос кому-то покажется глупым и бессмысленным, но для меня он имеет смыслНеобходима функция которая на входе...