Newtonsoft JSON.NET解析为自定义键/值对对象的数组

时间:2015-11-05 18:41:45

标签: c# json json.net

我在解析给定的JSON数据时遇到了这个奇怪的问题。我有这个JSON结构:

{"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]'.

编辑: 我也尝试将其保存到Dictionary:

 [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'."}

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

2 个答案:

答案 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);

答案 1 :(得分: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; }
}