将 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
。这是您的代码的唯一问题。
我需要从 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
。这是您的代码的唯一问题。