2015-11-05 79 views
1

我有解析给定的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'."} 

我做错了什么?谢谢您的回答。

+0

在Visual Studio中,您可以编辑 - >选择性粘贴 - >将JSON粘贴为类,然后DeserializeObject (stringData)。 – crashmstr

+0

这不是一个数组,它是一个最糟糕的词典,但是您应该能够声明一个Address类,其中的属性与JSON键相同,并直接进行反序列化。 @crashmstr的评论可能适用于较新版本的VS,但创建目标类也是一种选择。 – Eris

+0

我忘了补充说,但保存在字典中的数据也不工作。我将编辑我的问题:-) –

回答

1

看来,你要代表你的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; } 
} 
+0

谢谢!它完美的工作! –

3

你不需要重新发明轮子。这种功能性已经在起作用。 像下面创建类:

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); 
+0

谢谢,但这不是我所需要的。我从Web服务中获得这个JSON,将来可能会更改JSON。我需要双方(密钥和值)添加动态,因为它们来自服务器。 –