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

Deserialize JSON to C#: string and string[] values

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

例如:

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

我已经使用找到的 SingleValueArrayConverter 模式 on this post 实现了目标 class。这是说明我的问题的完整代码集:

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 正确解析为相同的 class?

更新:

我修改了 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;
    }
}

你可以改变这个:

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 等弱类型语言中是可能的)。

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