我正在使用Json.net反序列化并使用一个包含一到几千个JSON格式的Twitter推文的文件(将其视为单个JSON对象的大型文本文件存档)。为了简化这篇文章和插图的目的,我将每个Tweet的JSON减少到只有四行,但它们实际上有几十个属性。
我没有在JSON中创建包含推文的文件 - 它来自我无法控制或无法访问的数据库中的这种形式。以下是包含一条推文的文件示例:
{
"created_at": "Thu Mar 20 02:59:35 +0000 2009",
"text": "meet you later tonight"
}
以下是包含两条推文的文件示例:
{
"created_at": "Thu Mar 21 02:59:35 +0000 2010",
"text": "meet you later"
}
{
"created_at": "Thu Mar 22 02:59:35 +0000 2010",
"text": "see you tonight"
}
问题:当文件可能包含一个或多个JSON对象时,如何将文件反序列化为推文对象列表?
工作解决方案#1:即使文件只有一条推文,也将以JSON格式存储的推文消息转换为数组。此外,如果有多个推文,则需要在每条推文后插入一个逗号,但最后一条推文除外,以便将推文置于标准JSON格式。根据需要插入逗号后,以下代码可以正常工作:
JArray o = JArray.Parse("[" + textFileOfOneOrMoreTweetsInStandardJson + "]");
List<Tweet> tweets = JsonConvert.DeserializeObject <List<Tweet>> (o.ToString());
解决方案#2:根据需要放置逗号后,创建并使用覆盖读取的自定义转换器,并确定是否只有一条推文或多条推文,并根据该推文进行反序列化。使用此解决方案的示例: Deserializing JSON when sometimes array and sometimes object
我正在使用前者 - 使用解决方案#1而非解决方案#2的任何重大缺点?我不是在考虑替代解决方案吗?
未解决的问题:如何在反序列化之前针对模式验证各个JSON对象的存档?如果我像上面那样将对象视为数组,则在验证第一个对象并忽略其余对象后,json.net验证工具将停止。我想我可以以某种方式逐个浏览它们并在反序列化之前一次验证它们中的每一个,但这似乎是无效的并且会耗费时间,因为有成千上万的推文。
总而言之,JSON并不是真正设计用于在一个文本文件中将一个和几千个单独的JSON对象一起存档,因此使用该文件一直是一场艰苦的战斗。
旁注:我不关心序列化,我只需要通过将它们放入推文对象列表来分析LINQ的推文。在反序列化之前根据模式验证每条推文会很不错,但这可能太困难或耗费时间。
因为我无法在网络上的任何地方找到它并且它可以帮助其他人,所以我提供的是基于Twitter的API为Twitter推文创建的模式。但请注意,即使API反映字段不是可选字段,它们也在我的存档中,因此我需要指示可选的用于验证目的(令人惊讶的是,一条推文有可能包含许多JSON属性!)。
Twitter Tweet JSON架构
{
"$schema": "http://json-schema.org/schema#",
"description": "Twitter API JSON Schema",
"type": "object",
"properties": {
"annotations":{
"optional":true,
"type":[
"object",
"null"
],
"properties":{
}
},
"contributors":{
"optional":true,
"type":[
"array",
"null"
],
"type":[
"object",
"null"
],
"properties":{
"id":{
"optional":true,
"type":"integer"
},
"id_str":{
"optional":true,
"type":"string"
},
"screen_name":{
"optional":true,
"type":"string"
}
}
},
"coordinates":{
"optional":true,
"type":[
"object",
"null"
],
"properties":{
"coordinates":{
"optional":true,
"type":[
"array",
"number"
]
},
"type":{
"optional":true,
"type":"string"
}
}
},
"created_at":{
"optional":true,
"type":"string"
},
"entities":{
"optional":true,
"type":"object",
"properties":{
"hashtags":{
"optional":true,
"type":"object",
"type":"array",
"properties":{
"indices":{
"type":"array",
"type":"integer",
"type":"integer"
}
}
},
"media":{
"optional":true,
"type":"object",
"type":"array",
"properties":{
"display_url":{
"optional":true,
"type":"string"
},
"expanded_url":{
"optional":true,
"type":"string"
},
"id":{
"optional":true,
"type":"integer"
},
"id_str":{
"optional":true,
"type":"string"
},
"indices":{
"optional":true,
"type":"array",
"type":"integer"
},
"media_url":{
"optional":true,
"type":"string"
},
"media_url_https":{
"optional":true,
"type":"string"
},
"sizes":{
"optional":true,
"type":"object",
"properties":{
"thumb":{
"optional":true,
"type":"object",
"properties":{
"h":{
"optional":true,
"type":"integer"
},
"resize":{
"optional":true,
"type":"string"
},
"w":{
"optional":true,
"type":"integer"
}
}
},
"large":{
"optional":true,
"type":"object",
"properties":{
"h":{
"optional":true,
"type":"integer"
},
"resize":{
"optional":true,
"type":"string"
},
"w":{
"optional":true,
"type":"integer"
}
}
},
"medium":{
"optional":true,
"type":"object",
"properties":{
"h":{
"optional":true,
"type":"integer"
},
"resize":{
"optional":true,
"type":"string"
},
"w":{
"optional":true,
"type":"integer"
}
}
},
"small":{
"optional":true,
"type":"object",
"properties":{
"h":{
"optional":true,
"type":"integer"
},
"resize":{
"optional":true,
"type":"string"
},
"w":{
"optional":true,
"type":"integer"
}
}
}
}
}
}
},
"urls":{
"optional":true,
"type":"object",
"type":"array",
"properties":{
"display_url":{
"optional":true,
"type":"string"
},
"expanded_url":{
"optional":true,
"type":"string"
},
"indices":{
"optional":true,
"type":"array"
},
"url":{
"optional":true,
"type":"string"
}
}
},
"user_mentions":{
"optional":true,
"type":"object",
"type":"array",
"properties":{
"id":{
"optional":true,
"type":"integer"
},
"id_str":{
"optional":true,
"type":"string"
},
"indices":{
"optional":true,
"type":"array"
},
"name":{
"optional":true,
"type":"string"
},
"screen_name":{
"optional":true,
"type":"string"
}
}
}
}
},
"favorited":{
"optional":true,
"type":"boolean"
},
"geo":{
"optional":true,
"type":[
"object",
"null"
],
"properties":{
}
},
"id":{
"optional":true,
"type":"integer"
},
"id_str":{
"optional":true,
"type":"string"
},
"in_reply_to_screen_name":{
"optional":true,
"type":[
"string",
"null"
]
},
"in_reply_to_status_id":{
"optional":true,
"type":[
"integer",
"null"
]
},
"in_reply_to_status_id_str":{
"optional":true,
"type":[
"string",
"null"
]
},
"in_reply_to_user_id":{
"optional":true,
"type":[
"integer",
"null"
]
},
"in_reply_to_user_id_str":{
"optional":true,
"type":[
"string",
"null"
]
},
"place":{
"optional":true,
"type":[
"object",
"null"
],
"properties":{
"country":{
"optional":true,
"type":"string"
},
"country_code":{
"optional":true,
"type":"string"
},
"full_name":{
"optional":true,
"type":"string"
},
"id":{
"optional":true,
"type":"string"
},
"name":{
"optional":true,
"type":"string"
},
"place_type":{
"optional":true,
"type":"string"
},
"url":{
"optional":true,
"type":"string"
},
"bounding_box":{
"optional":true,
"type":"object",
"properties":{
"coordinates":{
"optional":true,
"type":"array",
"type":"array",
"type":"array",
"type":"array",
"type":"array"
},
"type":{
"optional":true,
"type":"string"
}
}
}
}
},
"retweet_count":{
"optional":true,
"type":[
"integer",
"string"
]
},
"retweeted":{
"optional":true,
"type":"boolean"
},
"source":{
"optional":true,
"type":"string"
},
"text":{
"optional":true,
"type":"string"
},
"truncated":{
"optional":true,
"type":"boolean"
},
"user":{
"optional":true,
"type":"object",
"properties":{
"contributors_enabled":{
"optional":true,
"type":"boolean"
},
"created_at":{
"optional":true,
"type":"string"
},
"default_profile":{
"optional":true,
"type":"boolean"
},
"default_profile_image":{
"optional":true,
"type":"boolean"
},
"description":{
"optional":true,
"type":[
"string",
"null"
]
},
"favourites_count":{
"optional":true,
"type":"integer"
},
"follow_request_sent":{
"optional":true,
"type":[
"boolean",
"null"
]
},
"following":{
"optional":true,
"type":[
"string",
"null"
]
},
"followers_count":{
"optional":true,
"type":"integer"
},
"friend_count":{
"optional":true,
"type":"integer"
},
"geo_enabled":{
"optional":true,
"type":"boolean"
},
"id":{
"optional":true,
"type":"integer"
},
"id_str":{
"optional":true,
"type":"string"
},
"is_translator":{
"optional":true,
"type":"boolean"
},
"lang":{
"optional":true,
"type":"string"
},
"listed_count":{
"optional":true,
"type":"integer"
},
"location":{
"optional":true,
"type":[
"string",
"null"
]
},
"name":{
"optional":true,
"type":"string"
},
"notifications":{
"optional":true,
"type":"null"
},
"profile_background_color":{
"optional":true,
"type":"string"
},
"profile_background_image_url":{
"optional":true,
"type":"string"
},
"profile_background_image_url_https":{
"optional":true,
"type":"string"
},
"profile_background_tile":{
"optional":true,
"type":"boolean"
},
"profile_image_url":{
"optional":true,
"type":"string"
},
"profile_image_url_https":{
"optional":true,
"type":"string"
},
"profile_link_color":{
"optional":true,
"type":"string"
},
"profile_sidebar_border_color":{
"optional":true,
"type":"string"
},
"profile_sidebar_fill_color":{
"optional":true,
"type":"string"
},
"profile_text_color":{
"optional":true,
"type":"string"
},
"profile_use_background_image":{
"optional":true,
"type":"boolean"
},
"protected":{
"optional":true,
"type":"boolean"
},
"screen_name":{
"optional":true,
"type":"string"
},
"show_all_inline_media":{
"optional":true,
"type":"boolean"
},
"statuses_count":{
"optional":true,
"type":"integer"
},
"time_zone":{
"optional":true,
"type":[
"string",
"null"
]
},
"url":{
"optional":true,
"type":[
"string",
"null"
]
},
"utc_offset":{
"optional":true,
"type":[
"integer",
"null"
]
},
"verified":{
"optional":true,
"type":"boolean"
}
}
}
}
}