将JSON反序列化为C#:string和string []值

时间:2015-05-21 17:52:40

标签: c# json serialization deserialization

我需要从REST服务中使用JSON。对于其中一个字段,服务有时会返回值的字符串,但有时会返回值的字符串数组。

例如:

var jsonArrayResponse = "{ \"id\" : \"abc\", \"relatedIds\" : [ \"def\", \"ghi\", \"jkl\" ] }";
var jsonSingleResponse = "{ \"id\" : \"123\", \"relatedIds\" : \"456\" }";

我使用找到on this post的SingleValueArrayConverter模式实现了目标类。以下是用于说明我的问题的完整代码集:

class Program
{
    static void Main(string[] args)
    {
        // this works fine with the SingleValueArrayConverter
        var jsonArray = "{ \"id\" : \"abc\", \"relatedIds\" : [ \"def\", \"ghi\", \"jkl\" ] }";
        var objArray = (MyObject)JsonConvert.DeserializeObject(jsonArray, typeof(MyObject));

        // this fails to parse "456" with Exception message:
        //      "Unable to cast object of type 'System.Object' to 
        //       type 'System.Collections.Generic.List`1[System.String]'."
        var jsonSingle = "{ \"id\" : \"123\", \"relatedIds\" : \"456\" }";
        var objSingle = (MyObject)JsonConvert.DeserializeObject(jsonSingle, typeof (MyObject));
    }
}

[DataContract]
public class MyObject
{
    [DataMember(Name = "id")]
    public string Id;

    [DataMember(Name = "relatedIds")]
    [JsonConverter(typeof(SingleValueArrayConverter<string>))]
    public List<string> RelatedIds;
}

public class SingleValueArrayConverter<T> : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object retVal = new Object();
        if (reader.TokenType == JsonToken.StartObject)
        {
            T instance = (T)serializer.Deserialize(reader, typeof(T));
            retVal = new List<T>() { instance };
        }
        else if (reader.TokenType == JsonToken.StartArray)
        {
            retVal = serializer.Deserialize(reader, objectType);
        }
        return retVal;
    }

    public override bool CanConvert(Type objectType)
    {
        return false;
    }
}

如何解决此问题以将两个不同的JSON blob正确解析为同一个类?

更新

我修改了SingleValueArrayConverter来检查字符串大小写(如下所述),并且事情开始起作用了。我使用转换器不仅仅是字符串,所以我必须添加第一个if语句,而不是按照建议修改它。)

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        object retVal = new Object();

        if (reader.TokenType == JsonToken.String)
        {
            string instance = (string)serializer.Deserialize(reader);
            retVal = new List<string> () {instance};
        }

        else if (reader.TokenType == JsonToken.StartObject)
        {
            T instance = (T)serializer.Deserialize(reader, typeof(T));
            retVal = new List<T>() { instance };
        }
        else if (reader.TokenType == JsonToken.StartArray)
        {
            retVal = serializer.Deserialize(reader, objectType);
        }
        return retVal;
    }

    public override bool CanConvert(Type objectType)
    {
        return false;
    }
}

2 个答案:

答案 0 :(得分:1)

你可以改变这个:

var jsonSingle = "{ \"id\" : \"123\", \"relatedIds\" : \"456\" }";

对此:

var jsonSingle = "{ \"id\" : \"123\", \"relatedIds\" : [ \"456\" ] }";

...并接受relatedIds有一个或多个项目,因此应该始终是一个集合。

或者,您可以将RelatedIds设为通用:

public class MyObject<T>
{
    // .... //

    public T RelatedIds;
}

您可以这样使用:

var objArray = (MyObject<List<string>>)JsonConvert.DeserializeObject(jsonArray, typeof(MyObject<List<string>>));

var objSingle = (MyObject<object>)JsonConvert.DeserializeObject(jsonSingle, typeof (MyObject<object>));

就个人而言,我个人更喜欢上一个选项。

你不能做的一件事就是定义为string在运行时突然变为List<string>(实际上也许你可以使用dynamic,但最好不要去那里...... )。在静态类型的语言中不允许这样做(尽管可以用弱类型语言,如JS或PHP)。

答案 1 :(得分:0)

JsonToken.StartObject更改为JsonToken.String。如果您在第object retVal = new Object();行上设置断点,则可以在第二次实例化期间看到reader.TokenTypeJsonToken.String。这是您的代码唯一的问题。

相关问题