我需要使用Newtonsoft.Json将json反序列化回对象实例。
但是,它是一个列表类型对象,该条目的键对我很有用。
我不知道如何在不手动逐个映射字段的情况下自动反序列化。
以下是回复:
internal class WhatToMineCalculatorsResponse
{
// Should be Dictionary???
[JsonProperty("coins")]
public IList<WhatToMineCalculatorResponse> Coins { get; set; }
}
internal class WhatToMineCalculatorResponse
{
// I want the key set in this field
public string Name { get; set; }
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("tag")]
public string Symbol { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("algorithm")]
public string Algo { get; set; }
[JsonProperty("listed")]
public bool IsListed { get; set; }
}
完整回复:https://whattomine.com/calculators.json
我对班级的最佳猜测是:
{{1}}
请注意,我希望我的课程中包含该键,但不是字典的键。以后很难找到密钥。
答案 0 :(得分:1)
您不能完全通过属性指定某些IList<T>
的{{1}}应序列化为JSON对象。正如其Serialization Guide中所解释的,Newtonsoft将字典和散列表映射到JSON对象,但将所有其他可枚举,列表和数组映射到JSON数组。相反,您必须使用custom JsonConverter
。
首先,定义以下转换器:
T
然后,您可以按如下方式反序列化:
internal class WhatToMineCalculatorResponseListConverter : KeyedListToJsonObjectConverterBase<WhatToMineCalculatorResponse>
{
protected override string KeyPropertyUnderlyingName => nameof(WhatToMineCalculatorResponse.Name);
}
public abstract class KeyedListToJsonObjectConverterBase<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType.IsArray)
return false;
return typeof(IList<T>).IsAssignableFrom(objectType);
}
protected abstract string KeyPropertyUnderlyingName { get; }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Get the key property name from the underlying name
var itemContract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
if (itemContract == null)
throw new JsonSerializationException(string.Format("type {0} is not serialized as a JSON object"));
var keyProperty = itemContract.Properties.Where(p => p.UnderlyingName == KeyPropertyUnderlyingName).SingleOrDefault();
if (keyProperty == null)
throw new JsonSerializationException(string.Format("Key property {0} not found", KeyPropertyUnderlyingName));
// Validate initial token.
if (reader.SkipComments().TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.StartObject)
throw new JsonSerializationException(string.Format("Unexpected token {0} at {1}", reader.TokenType, reader.Path));
// Allocate the List<T>. (It might be some subclass of List<T>, so use the default creator.
var list = existingValue as ICollection<T> ?? (ICollection<T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
// Process each key/value pair.
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.Comment:
break;
case JsonToken.EndObject:
return list;
case JsonToken.PropertyName:
{
// Get the name.
var name = (string)reader.Value;
reader.ReadAndAssert();
// Load the object
var jItem = JObject.Load(reader);
// Add the name property
jItem.Add(keyProperty.PropertyName, name);
// Deserialize the item and add it to the list.
list.Add(jItem.ToObject<T>(serializer));
}
break;
default:
{
throw new JsonSerializationException(string.Format("Unexpected token {0} at {1}", reader.TokenType, reader.Path));
}
}
}
// Should not come here.
throw new JsonSerializationException("Unclosed object at path: " + reader.Path);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Get the key property name from the underlying name
var itemContract = serializer.ContractResolver.ResolveContract(typeof(T)) as JsonObjectContract;
if (itemContract == null)
throw new JsonSerializationException(string.Format("type {0} is not serialized as a JSON object"));
var keyProperty = itemContract.Properties.Where(p => p.UnderlyingName == KeyPropertyUnderlyingName).SingleOrDefault();
if (keyProperty == null)
throw new JsonSerializationException(string.Format("Key property {0} not found", KeyPropertyUnderlyingName));
var converters = serializer.Converters.ToArray();
var list = (IEnumerable<T>)value;
writer.WriteStartObject();
foreach (var item in list)
{
var jItem = JObject.FromObject(item, serializer);
var name = (string)jItem[keyProperty.PropertyName];
jItem.Remove(keyProperty.PropertyName);
writer.WritePropertyName(name);
jItem.WriteTo(writer, converters);
}
writer.WriteEndObject();
}
}
public static partial class JsonExtensions
{
public static JsonReader SkipComments(this JsonReader reader)
{
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
public static void ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
{
new JsonReaderException(string.Format("Unexpected end at path {0}", reader.Path));
}
}
}
注意:
在序列化var settings = new JsonSerializerSettings
{
Converters = { new WhatToMineCalculatorResponseListConverter() },
};
var root = JsonConvert.DeserializeObject<WhatToMineCalculatorsResponse>(responseString, settings);
且类型KeyedListToJsonObjectConverterBase<T>
具有要用作JSON的特定属性的任何情况下,都可以重用基类转换器List<T>
对象属性名称。只需覆盖T
并返回实际的.Net属性名称(不是序列化名称)。
代码看起来有点复杂,因为我使KeyPropertyUnderlyingName
足够通用,可以处理密钥属性为只读的情况,例如:
KeyedListToJsonObjectConverterBase<T>
工作.Net小提琴here。