Newtonsoft JSON.NET解析到自定义键/值对对象的数组
我有解析给定的JSON数据这个奇怪的问题。我有这样的JSON结构:Newtonsoft JSON.NET解析到自定义键/值对对象的数组
{"value":[
{"street":"Karlova 25"},
{"city":"Prague"},
{"gpsLat":"50.1571"},
{"gpsLon":"15.0482"}
]}
如何使用Newtonsoft JSON.NET库解析这个结构呢?我试图用我自己的JsonConverter类:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer){
JArray jarray = (JArray)((JTokenReader)reader).CurrentToken;
List<AddressValue> values = new List<AddressValue>();
foreach (var jobj in jarray.Children<JObject>()){
foreach (JProperty prop in jobj.Properties()){
values.Add(new AddressValue() { Label = prop.Name, Value = prop.Value.ToString() });
}
}
return values.ToArray();
}
class AddressValue{
public string Label { get; set; }
public string Value { get; set; }
}
,但我有一个例外:
Exception thrown: 'Newtonsoft.Json.JsonSerializationException' in Newtonsoft.Json.DLL
Additional information: Unexpected token when deserializing object: StartObject. Path 'value[0]'.
编辑: 我也试过这个保存到词典:
[JsonProperty(PropertyName = "value")]
public Dictionary<string, string> Value{get; set;}
但我还有一个例外:
$exception {"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.String]' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.\r\nPath 'param.value'."}
我做错了什么?谢谢您的回答。
看来,你要代表你的JSON一个Dictionary<string, string>
作为对象的数组,其中每个嵌套的对象已经从字典一个键和值。您可以通过以下转炉做到这一点:
public class DictionaryToDictionaryListConverter<TKey, TValue> : JsonConverter
{
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
public override bool CanConvert(Type objectType)
{
if (Disabled)
return false;
return typeof(IDictionary<TKey, TValue>).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
var dict = (IDictionary<TKey, TValue>)(existingValue as IDictionary<TKey, TValue> ?? serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());
if (token.Type == JTokenType.Array)
{
foreach (var item in token)
using (var subReader = item.CreateReader())
serializer.Populate(subReader, dict);
}
else if (token.Type == JTokenType.Object)
{
using (var subReader = token.CreateReader())
serializer.Populate(subReader, dict);
}
return dict;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dict = (IDictionary<TKey, TValue>)value;
// Prevent infinite recursion of converters
using (new PushValue<bool>(true,() => Disabled, val => Disabled = val))
{
serializer.Serialize(writer, dict.Select(p => new[] { p }.ToDictionary(p2 => p2.Key, p2 => p2.Value)));
}
}
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
}
然后按如下方式使用它在你的容器类:
public class RootObject
{
[JsonProperty("value")]
[JsonConverter(typeof(DictionaryToDictionaryListConverter<string, string>))]
public Dictionary<string, string> Value { get; set; }
}
注意,在读取时转换器将抛出一个异常,如果该键不唯一。
更新
对于AddressValue
,你可以使用下面的转换器:
public class AddressValueConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(AddressValue);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var addressValue = (existingValue as AddressValue ?? new AddressValue());
var token = JObject.Load(reader);
var property = token.Properties().SingleOrDefault();
if (property != null)
{
addressValue.Label = property.Name;
addressValue.Value = (string)property.Value;
}
return addressValue;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var addressValue = (AddressValue)value;
serializer.Serialize(writer, new Dictionary<string, string> { { addressValue.Label, addressValue.Value } });
}
}
然后按如下方式使用它:
[JsonConverter(typeof(AddressValueConverter))]
public class AddressValue
{
public string Label { get; set; }
public string Value { get; set; }
}
public class RootObject
{
[JsonProperty("value")]
public List<AddressValue> Value { get; set; }
}
谢谢!它完美的工作! –
你不需要重新发明轮子。这种功能性已经在起作用。 像下面创建类:
public class Value
{
public string street { get; set; }
public string city { get; set; }
public string gpsLat { get; set; }
public string gpsLon { get; set; }
}
public class MyClass
{
public List<Value> value { get; set; }
}
现在,你可以简单地反序列化JSON你到你的POCO对象。
MyClass result = JsonConvert.DeserializeObject<MyClass>(youJson);
谢谢,但这不是我所需要的。我从Web服务中获得这个JSON,将来可能会更改JSON。我需要双方(密钥和值)添加动态,因为它们来自服务器。 –
在Visual Studio中,您可以编辑 - >选择性粘贴 - >将JSON粘贴为类,然后DeserializeObject(stringData)。 –
crashmstr
这不是一个数组,它是一个最糟糕的词典,但是您应该能够声明一个Address类,其中的属性与JSON键相同,并直接进行反序列化。 @crashmstr的评论可能适用于较新版本的VS,但创建目标类也是一种选择。 – Eris
我忘了补充说,但保存在字典中的数据也不工作。我将编辑我的问题:-) –