有时是数组有时是对象时反序列化 JSON
我在使用 JSON.NET 库反序列化从 Facebook 返回的数据时遇到了一些麻烦。
从简单的墙贴返回的 JSON 看起来像:
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
照片返回的 JSON 看起来像:
"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"}}
],
一切都很好,我没有任何问题。我现在遇到了来自移动客户端的带有以下 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; }
}
如果不使用 FacebookMediaJsonConverter,我会收到错误: 无法反序列化JSON 对象转换为“System.Collections.Generic.List`1[FacebookMedia]”类型。 这是有道理的,因为在 JSON 中,Media 不是一个集合。
我发现这篇文章描述了类似的问题,所以我尝试走这条路线: 反序列化 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
return null;
}
工作正常,但我现在得到一个新的异常:
Inside JsonSerializerInternalReader.cs ,CreateValueInternal():反序列化对象时出现意外标记:PropertyName
reader.Value 的值为“permalink”。我可以在开关中清楚地看到 JsonToken.PropertyName 没有大小写。
我需要在转换器中做一些不同的事情吗?感谢您的任何帮助。
I'm having a bit of trouble deserializing data returned from Facebook using the JSON.NET libraries.
The JSON returned from just a simple wall post looks like:
{
"attachment":{"description":""},
"permalink":"http://www.facebook.com/permalink.php?story_fbid=123456789"
}
The JSON returned for a photo looks like:
"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"}}
],
Everything works great and I have no problems. I've now come across a simple wall post from a mobile client with the following JSON, and deserialization now fails with this one single post:
"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"
Here is the class I am deserializing as:
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; }
}
Without using the FacebookMediaJsonConverter, I get an error: Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[FacebookMedia]'.
which makes sense, since in the JSON, Media is not a collection.
I found this post which describes a similar problem, so I've attempted to go down this route: Deserialize JSON, sometimes value is an array, sometimes "" (blank string)
My converter looks like:
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;
}
Which works fine, except I now get a new exception:
Inside JsonSerializerInternalReader.cs, CreateValueInternal(): Unexpected token while deserializing object: PropertyName
The value of reader.Value is "permalink". I can clearly see in the switch that there's no case for JsonToken.PropertyName.
Is there something I need to do differently in my converter? Thanks for any help.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
有关如何处理这种情况的非常详细的说明,请访问 “使用用于修复错误 JSON 结果的自定义 JsonConverter”。
总而言之,您可以扩展默认的 JSON.NET 转换器,
用问题注释属性
扩展转换器以返回所需类型的列表,即使对于单个对象也是如此
正如文章中提到的,此扩展并不完全通用,但如果您愿意获取列表,它就可以工作。
A very detailed explanation on how to handle this case is available at "Using a Custom JsonConverter to fix bad JSON results".
To summarize, you can extend the default JSON.NET converter doing
Annotate the property with the issue
Extend the converter to return a list of your desired type even for a single object
As mentioned in the article this extension is not completely general but it works if you are fine with getting a list.
JSON.NET 的开发人员最终为项目 Codeplex 网站提供了帮助。解决方案如下:
问题是,当它是 JSON 对象时,我没有读取该属性。这是正确的代码:
James 也很友善地为上述方法提供了单元测试。
The developer of JSON.NET ended up helping on the projects codeplex site. Here is the solution:
The problem was, when it was a JSON object, I wasn't reading past the attribute. Here is the correct code:
James was also kind enough to provide unit tests for the above method.
根据上面 Camilo Martinez 的回答,这是一种更现代、类型安全、更精简和完整的方法,使用 JsonConverter 的通用版本和 C# 8.0 以及实现序列化部分。除了根据问题预期的两个标记之外,它还会引发异常。代码永远不应该做超出要求的事情,否则您将面临由于错误处理意外数据而导致未来错误的风险。
然后这样装饰属性:
我已将属性类型从
List<>
更改为ICollection<>
,因为 JSON POCO 通常只需要这种较弱的类型,但如果需要List<>
,则只需将上述所有代码中的ICollection
和Collection
替换为List
。Based on Camilo Martinez's answer above, this is a more modern, type-safe, leaner and complete approach using the generic version of
JsonConverter
and C# 8.0 as well as implementing the serialization part. It also throws an exception for tokens other than the two expected according to the question. Code should never do more than required otherwise you run the risk of causing a future bug due to mishandling unexpected data.And then decorate the property thus:
I've changed the property type from
List<>
toICollection<>
as a JSON POCO typically need only be this weaker type, but ifList<>
is required, then just replacedICollection
andCollection
withList
in all the above code.阐述 Martinez 和 mfanto 对 Newtonsoft 的回答。它确实可以与 Newtonsoft 一起使用:
下面是一个使用数组而不是列表(并且命名正确)的示例。
然后在属性上写下:
Newtonsoft 将为您完成剩下的工作。
为马丁内斯和姆凡托干杯!
不管你相信与否,这适用于子项目。 (甚至可能必须这样做。)所以...在我的 InsuranceInfo 中,如果我有另一个对象/数组混合体,请在该属性上再次使用它。
这还允许您将对象重新序列化回 json。当它重新序列化时,它将始终是一个数组。
Expounding upon Martinez and mfanto's answer for Newtonsoft. It does work with Newtonsoft:
Here is an example of doing it with an array instead of a list (and correctly named).
Then over the attribute write this:
Newtonsoft will do the rest for you.
Cheers to Martinez and mfanto!
Believe it or not, this will work with sub items. (It may even have to.) So... inside of my InsuranceInfo, if I have another object/array hybrid, use this again on that property.
This will also allow you to reserialize the object back to json. When it does reserialize, it will always be an array.
看一下 C# 框架中的 System.Runtime.Serialization 命名空间,它将带您快速到达您想要的位置。
如果需要,您可以查看此项目中的一些示例代码(不尝试插入我自己的工作,但我刚刚完成了与您正在做的几乎完全相同的工作,但使用了不同的源 API
希望它有所帮助。
take a look at the System.Runtime.Serialization namespace in the c# framework, it's going to get you to where you want to be very quickly.
If you want, you can check out some example code in this project (not trying to plug my own work but i just finished pretty much exactly what you are doing but with a different source api.
hope it helps.
.Net框架
注意:
“对象”必须根据您的响应的 Json 属性进行定义
.Net Framework
Note:
"Object" must be defined according to the Json attributes of your response
我认为你应该这样写你的课程......!!!
I think you should write your class like this...!!!