C# 有时是数组有时是对象时反序列化JSON
我在使用JSON.NET库反序列化Facebook返回的数据时遇到了一些问题 从一个简单的墙柱返回的JSON如下所示:C# 有时是数组有时是对象时反序列化JSON,c#,json,json.net,facebook-c#-sdk,json-deserialization,C#,Json,Json.net,Facebook C# Sdk,Json Deserialization,我在使用JSON.NET库反序列化Facebook返回的数据时遇到了一些问题 从一个简单的墙柱返回的JSON如下所示: { "attachment":{"description":""}, "permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789" } "attachment":{ "media":[ { "href":"
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
"attachment":{
"media":[
{
"href":"http://www.facebook.com/photo.php?fbid=12345",
"alt":"",
"type":"photo",
"src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg",
"photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}}
],
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
return serializer.Deserialize<List<FacebookMedia>>(reader);
else
return null;
}
为照片返回的JSON如下所示:
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
"attachment":{
"media":[
{
"href":"http://www.facebook.com/photo.php?fbid=12345",
"alt":"",
"type":"photo",
"src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg",
"photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}}
],
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
return serializer.Deserialize<List<FacebookMedia>>(reader);
else
return null;
}
一切都很好,我没有问题。现在,我遇到了一个来自移动客户端的带有以下JSON的简单墙贴,反序列化现在因这篇文章而失败:
"attachment":
{
"media":{},
"name":"",
"caption":"",
"description":"",
"properties":{},
"icon":"http://www.facebook.com/images/icons/mobile_app.gif",
"fb_object_type":""
},
"permalink":"http://www.facebook.com/1234"
下面是我反序列化为的类:
public class FacebookAttachment
{
public string Name { get; set; }
public string Description { get; set; }
public string Href { get; set; }
public FacebookPostType Fb_Object_Type { get; set; }
public string Fb_Object_Id { get; set; }
[JsonConverter(typeof(FacebookMediaJsonConverter))]
public List<FacebookMedia> { get; set; }
public string Permalink { get; set; }
}
公共类facebook附件
{
公共字符串名称{get;set;}
公共字符串说明{get;set;}
公共字符串Href{get;set;}
PublicFaceBookPostType Fb_对象_类型{get;set;}
公共字符串Fb_Object_Id{get;set;}
[JsonConverter(类型(FacebookMediaJsonConverter))]
公共列表{get;set;}
公共字符串Permalink{get;set;}
}
如果不使用FacebookMediaJsonConverter,我会遇到一个错误:无法将JSON对象反序列化为“System.Collections.Generic.List`1[FacebookMedia]”类型。
这是有道理的,因为在JSON中,媒体不是集合
我发现这篇文章描述了一个类似的问题,所以我尝试走这条路:
我的转换器看起来像:
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
"attachment":{
"media":[
{
"href":"http://www.facebook.com/photo.php?fbid=12345",
"alt":"",
"type":"photo",
"src":"http://photos-b.ak.fbcdn.net/hphotos-ak-ash1/12345_s.jpg",
"photo":{"aid":"1234","pid":"1234","fbid":"1234","owner":"1234","index":"12","width":"720","height":"482"}}
],
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
return serializer.Deserialize<List<FacebookMedia>>(reader);
else
return null;
}
public override object ReadJson(JsonReader reader,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(reader.TokenType==JsonToken.StartArray)
返回序列化程序。反序列化(读取器);
其他的
返回null;
}
这很好,但我现在有一个新的例外:
在JsonSerializerInternalReader.cs内部,CreateValueInternal():反序列化对象时出现意外标记:PropertyName
reader的值。值为“permalink”。我可以在开关中清楚地看到,没有必要使用JsonToken.PropertyName
在我的转换器中有什么需要做的不同吗?谢谢你的帮助 看看c#framework中的System.Runtime.Serialization名称空间,它会让您很快找到想要的位置 如果您愿意,您可以查看project中的一些示例代码(不尝试插入我自己的工作,但我刚刚完成了您正在做的工作,但使用了不同的源api)
希望有帮助。JSON.NET的开发人员最终在projects codeplex网站上提供了帮助。以下是解决方案: 问题是,当它是JSON对象时,我没有读取属性。下面是正确的代码:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<List<FacebookMedia>>(reader);
}
else
{
FacebookMedia media = serializer.Deserialize<FacebookMedia>(reader);
return new List<FacebookMedia>(new[] {media});
}
}
public override object ReadJson(JsonReader reader,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
if(reader.TokenType==JsonToken.StartArray)
{
返回序列化程序。反序列化(读取器);
}
其他的
{
FacebookMedia=序列化程序。反序列化(读取器);
返回新列表(新[]{media});
}
}
James也很友好地为上述方法提供了单元测试。我认为你应该这样编写你的类
public class FacebookAttachment
{
[JsonProperty("attachment")]
public Attachment Attachment { get; set; }
[JsonProperty("permalink")]
public string Permalink { get; set; }
}
public class Attachment
{
[JsonProperty("media")]
public Media Media { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("caption")]
public string Caption { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("properties")]
public Properties Properties { get; set; }
[JsonProperty("icon")]
public string Icon { get; set; }
[JsonProperty("fb_object_type")]
public string FbObjectType { get; set; }
}
public class Media
{
}
public class Properties
{
}
有关如何处理此案件的详细说明,请访问 总之,您可以扩展默认的JSON.NET转换器
[JsonConverter(typeof(SingleValueArrayConverter<OrderItem>))]
public List<OrderItem> items;
[JsonConverter(typeof(SingleValueArrayConverter))]
公共清单项目;
public class SingleValueArrayConverter<T> : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
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 true;
}
}
公共类SingleValueArrayConverter:JsonConverter
{
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新的NotImplementedException();
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
object retVal=新对象();
if(reader.TokenType==JsonToken.StartObject)
{
反序列化(reader,typeof(T));
retVal=new List(){instance};
}else if(reader.TokenType==JsonToken.StartArray){
retVal=序列化程序。反序列化(读取器,对象类型);
}
返回返回;
}
公共覆盖布尔CanConvert(类型objectType)
{
返回true;
}
}
正如文章中提到的,此扩展并不完全通用,但如果您对获取列表很满意,它就可以工作。基于卡米洛·马丁内斯的上述回答,这是一种更现代、更安全、更精简、更完整的方法,使用了
JsonConverter
的通用版本和C#8.0,并实现了序列化部分对于问题中预期的两个令牌之外的令牌,也会引发异常。代码不应执行超出要求的操作,否则您可能会由于错误处理意外数据而导致将来的错误
internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<ICollection<T>> where T : class, new()
{
public override void WriteJson(JsonWriter writer, ICollection<T> value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value);
}
public override ICollection<T> ReadJson(JsonReader reader, Type objectType, ICollection<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return reader.TokenType switch
{
JsonToken.StartObject => new Collection<T> {serializer.Deserialize<T>(reader)},
JsonToken.StartArray => serializer.Deserialize<ICollection<T>>(reader),
_ => throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.")
};
}
}
内部类SingleObjectOrArrayJsonConverter:JsonConverter其中T:class,new()
{
公共重写void WriteJson(JsonWriter编写器、ICollection值、JsonSerializer序列化器)
{
serializer.Serialize(writer,value.Count==1?(对象)value.Single():value);
}
公共重写ICollection ReadJson(JsonReader阅读器,类型objectType,ICollection existingValue,bool hasExistingValue,JsonSerializer序列化程序)
{
返回reader.TokenType开关
{
JsonToken.StartObject=>新集合{serializer.Deserialize(reader)},
JsonToken.StartArray=>serializer.Deserialize(reader),
_=>抛出新ArgumentOutOfRangeException($“转换器不支持JSON令牌类型{reader.TokenType}。”)
};
}
}
然后对物业进行如下装饰:
[JsonConverter(typeof(SingleObjectOrArrayJsonConverter<OrderItem>))]
public ICollection<OrderItem> items;
[JsonConverter(typeof(SingleObjectOrArrayJsonConverter))]
公共信息收集项目;
我已经将属性类型从List
更改为ICollection
,因为JSON POCO通常只需要这种较弱的类型,但是如果需要List
,