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

本文关键字:string JSON 反序列化 | 更新日期: 2023-09-27 17:50:15

我需要从REST服务中使用JSON。对于其中一个字段,服务有时为该值返回字符串,而其他时候为该值返回字符串数组。

例如:

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

我已经实现了目标类使用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;
    }
}

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

你可以改变这个:

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.TokenTypeJsonToken.String。这是你代码的唯一问题。