我必须处理的API在数组中只有一个条目时返回一个对象,否则返回一个正确的数组。这是一个例子:
只有一个条目:
{
"product": {
"offers": {
"id": 1,
"price": 55.6
}
}
}
不止一个条目:
{
"product": {
"offers": [
{
"id": 1,
"price": 55.6
},
{
"id": 2,
"price": 34.6
},
]
}
}
是否有某种方法可以编写代码,将这两种变体反序列化为数组而无需为整个(深层嵌套)响应编写完整的JsonConverter
?除了这个奇怪的看似设计决策,JSON.net可以轻松地反序列化它,所以我想知道是否有JSON.net功能允许我写这样的东西:
if (hasExpectedArray && hasEncounteredObject) {
deserialized.property = new List<T>();
deserialized.property.Add(objectEncountered);
}
我的另一个想法是预先解析JSON并使用一些搜索和替换功能来将所有可以是数组的对象更改为数组。但这看起来很脏又脆。
编辑:实际上忘记了第三种情况:它也可以是一个字符串,或者其中一个数组成员可以是一个字符串:
{
"product": {
"offers": "This is an offer"
}
}
{
"product": {
"offers": [
{
"id": 1,
"price": 55.6
},
"This is another offer"
]
}
}
答案 0 :(得分:8)
发布此消息后不久,我在搜索结果中找到了一个可以解决这个问题的博客文章:
http://michaelcummings.net/mathoms/using-a-custom-jsonconverter-to-fix-bad-json-results/
编辑2017-07-19:弹出另一个博文:http://trycatchfail.com/blog/post/Dealing-with-Horrid-No-Good-Very-Bad-APIs-Using-JSONNET
如果它发生故障,这里是JsonConverter的相关代码:
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 interface IHasContent
{
string Content { get; set; }
}
因为至少只有字符串的类型总是具有相同的名称。
有问题的类型得到了属性和接口:
[JsonConverter(typeof(SingleValueArrayConverter<ShippingInfo>))]
public class ShippingInfo : IHasContent
对于字符串转换,我添加了一个简单的隐式运算符:
public static implicit operator ShippingInfo(string s)
{
return new ShippingInfo { Content = s };
}
转换器有一些类型限制:
public class SingleValueArrayConverter<T> : JsonConverter where T : IHasContent, new()
最后,这是ReadJson方法:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal;
switch (reader.TokenType)
{
case JsonToken.StartObject:
var instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T> { instance };
break;
case JsonToken.StartArray:
retVal = serializer.Deserialize(reader, objectType);
break;
case JsonToken.String:
retVal = ReadStringAsContentObject();
break;
default:
throw new ArgumentException();
}
return retVal;
object ReadStringAsContentObject()
{
var content = new T();
content.Content = reader.ReadAsString();
var returnObject = new List<T> { content };
return returnObject;
}
}