如何使用Newtonsoft反序列化既可以是数组也可以是字典的对象?

6

我正在使用一个返回json对象的API,我需要对其进行反序列化。我的问题是,这些对象中的一个成员有时是空数组(“[]”),有时是字典(“{“1”:{...},“2”:{...}}”)。我希望将其反序列化为数组或字典,因为我并不关心ID,我只想要所有对象的列表。以下是我如何反序列化该对象:

var response = JsonConvert.DeserializeObject<Response>(json);

以下是Response类的定义:

public class Response
{
    [JsonProperty(PropertyName = "variations")]
    public Dictionary<int, Variation> Variations { get; set; }
}

当响应中的variations字段包含字典时,它可以很好地工作,但是当它包含空数组时,它会失败。我收到了来自Newtonsoft的错误,说数组无法反序列化为字典。如果我将Variations属性定义为数组,则对于空数组,它可以正常工作,但是当它是字典时,它会失败。我应该怎么做才能正确地反序列化两个可能的值,或者在它是一个数组时忽略空数组并将Variations设置为null而不是失败。

谢谢。


2
哎呀,这是个糟糕的源代码 :( 我打赌它是来自 PHP,对吧? - user166390
我看到有人建议在转换之前进行“文本替换”,但我认为也可以通过自定义的JsonConverter来完成。 - user166390
@pst:这是个好主意,我没想到过这么做。 - Carl
如果提到这段文本,我不认为我会称其为“伟大”,但也许可以说是“hack-ish并在紧急情况下工作”。很容易想象出退化的数据... 如果在.NET4中,我相信可以使用带有包装器访问器的dynamic(但我不使用.NET4或dynamic)。 - user166390
很可能只用对象而不是动态类型也可以工作。我会在周二尝试一下(那之前我不会上班),然后告诉你结果。感谢你的帮助! - Carl
显示剩余4条评论
3个回答

9

这里提供一种使用Json转换器的另一种解决方案

public class Response
{
    [JsonProperty(PropertyName = "variations")]
    [JsonConverter(typeof(EmptyArrayOrDictionaryConverter))]
    public Dictionary<int, Variation> Variations { get; set; }
}


public class EmptyArrayOrDictionaryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(Dictionary<string, object>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Object)
        {
            return token.ToObject(objectType, serializer);
        }
        else if (token.Type == JTokenType.Array)
        {
            if (!token.HasValues)
            {
                // create empty dictionary
                return Activator.CreateInstance(objectType);
            }
        }

        throw new JsonSerializationException("Object or empty array expected");
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

太好了!我到处都在找这个。这比字符串替换的方法要好得多。 - Etienne Charland

2

这是对Carl示例的一种变化(抱歉用了双关语)。我有类似的需求,但是不是返回字典,而是需要一个数组。我使用的API说它返回一个数组。但是,在结果中只有一个项目的情况下,它将以对象的形式返回!

public class VarationsContainer
{
    [JsonIgnore]
    public Varation[] Varations
    {
        get
        {
            return ParseObjectToArray<Variation>(VariationObject);
        }
    }

    [JsonProperty(PropertyName = "varations")]
    public object VarationsObject { get; set; }

    protected T[] ParseObjectToArray<T>(object ambiguousObject)
    {
        var json = ambiguousObject.ToString();
        if (String.IsNullOrWhiteSpace(json))
        {
            return new T[0]; // Could return null here instead.
        }
        else if (json.TrimStart().StartsWith("["))
        {
            return JsonConvert.DeserializeObject<T[]>(json);
        }
        else
        {
            return new T[1] { JsonConvert.DeserializeObject<T>(json) };
        }
    }
}

我希望这对其他不开心的API使用者有所帮助。

+1,节省了我很多麻烦,感谢。对于对象/数组(在我的情况下是ToList()),它运行得非常完美。 - Robin Rieger

1

这是我使用的解决方案:

    public Dictionary<int, Variation> Variations
    {
        get
        {
            var json = this.VariationsJson.ToString();
            if (json.RemoveWhiteSpace() == EmptyJsonArray)
            {
                return new Dictionary<int, Variation>();
            }
            else
            {
                return JsonConvert.DeserializeObject<Dictionary<int, Variation>>(json);
            }
        }
    }

    [JsonProperty(PropertyName = "variations")]
    public object VariationsJson { get; set; }

基本上,变量首先被反序列化为一个基本对象。当我想要读取值时,我会检查对象是否为空数组,如果是,则返回一个空字典。如果对象是一个好的字典,我会将其反序列化并返回。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接