使用codable的Swift JSON解码发现了一个字符串/数据
我正在玩《纽约时报》的API,我得到了消息 类型不匹配(Swift.Array,Swift.DecodingError.Context)(编码路径: [编码键(stringValue:“结果”,intValue:无), _JSONKey(stringValue:“索引3”,intValue:3),codingkey(stringValue:“多媒体”,intValue:nil)],debugDescription:“预期解码 数组,但找到了一个字符串/数据。“,underyingError:nil)) 当JSON的多媒体部分是字符串而不是数组时会发生这种情况:类似于这个问题: 所以我决定做一个最低限度的例子 附文章使用codable的Swift JSON解码发现了一个字符串/数据,swift,codable,Swift,Codable,我正在玩《纽约时报》的API,我得到了消息 类型不匹配(Swift.Array,Swift.DecodingError.Context)(编码路径: [编码键(stringValue:“结果”,intValue:无), _JSONKey(stringValue:“索引3”,intValue:3),codingkey(stringValue:“多媒体”,intValue:nil)],debugDescription:“预期解码 数组,但找到了一个字符串/数据。“,underyingError:ni
public struct Article : Codable {
var abstract: String?
var thumbnail_standard: String?
var multimedia: [Multimedia]?
var title: String?
var url: URL?
private enum CodingKeys: String, CodingKey {
case abstract = "abstract"
case multimedia = "multimedia"
case thumbnail_standard = "thumbnail_standard"
case title = "title"
case url = "url"
}
}
多媒体
struct Multimedia: Codable {
var url: String?
private enum CodingKeys: String, CodingKey {
case url = "url"
}
}
我可以使用JSON字符串
let jsonString = """
{
"slug_name": "30dc-emoluments",
"section": "U.S.",
"subsection": "Politics",
"title": "Congressional Democrats’ Lawsuit Examining Trump’s Private Business Can Proceed, Federal Judge Says",
"abstract": "The decision is at least a temporary victory for the president’s critics who say he is willfully flaunting constitutional bans.",
"url": "https://www.nytimes.com/2019/04/30/us/politics/trump-emoluments-clauses.html",
"byline": "By SHARON LaFRANIERE",
"thumbnail_standard": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-thumbStandard.jpg",
"item_type": "Article",
"source": "The New York Times",
"updated_date": "2019-04-30T22:09:45-04:00",
"created_date": "2019-04-30T21:56:05-04:00",
"published_date": "2019-04-29T20:00:00-04:00",
"first_published_date": "2019-04-30T21:54:34-04:00",
"material_type_facet": "News",
"kicker": null,
"subheadline": null,
"des_facet": "",
"org_facet": [
"Democratic Party",
"Constitution (US)",
"Justice Department",
"Trump International Hotel (Washington, DC)"
],
"per_facet": [
"Sullivan, Emmet G",
"Trump, Donald J"
],
"geo_facet": "",
"related_urls": [
{
"suggested_link_text": "Appeals Court Judges Appear Skeptical of Emoluments Case Against Trump",
"url": "https://www.nytimes.com/2019/03/19/us/politics/trump-emoluments-lawsuit.html"
},
{
"suggested_link_text": "Democrats in Congress Sue Trump Over Foreign Business Dealings",
"url": "https://www.nytimes.com/2017/06/14/us/politics/democrats-in-congress-to-sue-trump-over-foreign-business-dealings.html"
}
],
"multimedia": [
{
"url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-thumbStandard.jpg",
"format": "Standard Thumbnail",
"height": 75,
"width": 75,
"type": "image",
"subtype": "photo",
"caption": "The Trump International Hotel in Washington.",
"copyright": "Gabriella Demczuk for The New York Times"
},
{
"url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-articleInline.jpg",
"format": "Normal",
"height": 130,
"width": 190,
"type": "image",
"subtype": "photo",
"caption": "The Trump International Hotel in Washington.",
"copyright": "Gabriella Demczuk for The New York Times"
},
{
"url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-mediumThreeByTwo210.jpg",
"format": "mediumThreeByTwo210",
"height": 140,
"width": 210,
"type": "image",
"subtype": "photo",
"caption": "The Trump International Hotel in Washington.",
"copyright": "Gabriella Demczuk for The New York Times"
},
{
"url": "https://static01.nyt.com/images/2019/04/19/world/30dc-emoluments/30dc-emoluments-mediumThreeByTwo440.jpg",
"format": "mediumThreeByTwo440",
"height": 293,
"width": 440,
"type": "image",
"subtype": "photo",
"caption": "The Trump International Hotel in Washington.",
"copyright": "Gabriella Demczuk for The New York Times"
}
]
}
"""
代码很好:
if let data = jsonString.data(using: .utf8)
{
let decoder = JSONDecoder()
let result = try? decoder.decode(Article.self, from: data)
print(result)
}
但是,以下JSON字符串被解码为nill:
let jsonString = """
{
"slug_name": "01a3_quote-web",
"section": "Today’s Paper",
"subsection": "",
"title": "Quotation of the Day: Who Killed Atlanta’s Children? Retesting Evidence After 40 Years",
"abstract": "Quotation of the Day for Wednesday, May 1, 2019.",
"url": "https://www.nytimes.com/2019/04/30/todayspaper/quotation-of-the-day-who-killed-atlantas-children-retesting-evidence-after-40-years.html",
"byline": "",
"thumbnail_standard": "",
"item_type": "Article",
"source": "The New York Times",
"updated_date": "2019-04-30T21:26:36-04:00",
"created_date": "2019-04-30T21:26:36-04:00",
"published_date": "2019-04-29T20:00:00-04:00",
"first_published_date": "2019-04-30T21:25:06-04:00",
"material_type_facet": "Quote",
"kicker": null,
"subheadline": null,
"des_facet": "",
"org_facet": "",
"per_facet": "",
"geo_facet": "",
"related_urls": null,
"multimedia": ""
}
"""
尽管我将对象中的属性设置为可选,但在decoder.decode方法中使用了.self
如何获取要解码的第二个JSON字符串?解决方案
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let abstract = try container.decode(String.self, forKey: .abstract)
let thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
var multimedia: [Multimedia] = []
do {
multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
} catch {}
let title = try container.decode(String.self, forKey: .title)
let url = try container.decode(URL.self, forKey: .url)
self.init(abstract: abstract, thumbnailStandard: thumbnailStandard, multimedia: multimedia, title: title, url: url)
}
编辑
试试这个
struct Article {
let abstract: String
let thumbnailStandard: String
let multimedia: [Multimedia]
let title: String
let url: URL
}
extension Article: Decodable {
enum CodingKeys: String, CodingKey {
case abstract
case thumbnailStandard = "thumbnail_standard"
case multimedia
case title
case url
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let abstract = try container.decode(String.self, forKey: .abstract)
let thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
let multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
let title = try container.decode(String.self, forKey: .title)
let url = try container.decode(URL.self, forKey: .url)
self.init(abstract: abstract, thumbnailStandard: thumbnailStandard, multimedia: multimedia, title: title, url: url)
}
}
struct Multimedia: Codable {
let url: String
}
参考:好吧,这种不一致性应该由api来处理。但通过引入
enum
,您可以优雅地处理不同类型的返回类型,如下所示
enum MultiMediaType: Codable {
case string(String)
case array(Array<Multimedia>)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
self = .array(try container.decode([Multimedia].self))
} catch DecodingError.typeMismatch {
self = .string(try container.decode(String.self))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let value):
try container.encode(value)
case .array(let value):
try container.encode(value)
}
}
}
public struct Article : Codable {
var abstract: String?
var thumbnail_standard: String?
var multimedia: MultiMediaType
var title: String?
var url: URL?
}
枚举多媒体类型:可编码{
大小写字符串(字符串)
案例数组(数组)
init(来自解码器:解码器)抛出{
let container=尝试解码器。singleValueContainer()
做{
self=.array(尝试container.decode([Multimedia].self))
}捕获解码错误。类型不匹配{
self=.string(尝试container.decode(string.self))
}
}
func encode(到编码器:编码器)抛出{
var container=encoder.singleValueContainer()
切换自身{
case.string(let值):
尝试container.encode(值)
case.array(let值):
尝试container.encode(值)
}
}
}
公共结构文章:可编码{
var摘要:字符串?
var-u标准:字符串?
多媒体:多媒体类型
变量标题:字符串?
var-url:url?
}
解决方案是手动解码所有值可以是不同类型的键 在此示例中,
multimedia
是可选的([multimedia]
或nil
),而perFacet
是非可选的[String]
,如果值为空字符串,则为空
所有结构成员都是常量(let
),并且添加了convertFromSnakeCase
策略以去除蛇壳名称
如果多媒体仍返回零,则应返回
[]
而不是”
是,我们可以写信给《纽约时报》投诉。或者,可能接受它返回“”并创建一个解决方案。我没有使用它,但一个可能的解决方案是使用AnyCodable库()并将多媒体
字段定义为AnyDecodable
。这需要一些额外的工作才能正确提取多媒体数据(即转换为[multimedia]
,如果转换失败,则您知道它是空的或“”),但其余的转换应在任何一种情况下都能工作。这与实现的解决方案最接近。
struct Article : Decodable {
let abstract: String
let thumbnailStandard: String
let multimedia: [Multimedia]?
let perFacet : [String]
let title: String
let url: URL
private enum CodingKeys: String, CodingKey {
case abstract, multimedia, thumbnailStandard, title, url, perFacet
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
abstract = try container.decode(String.self, forKey: .abstract)
thumbnailStandard = try container.decode(String.self, forKey: .thumbnailStandard)
do {
perFacet = try container.decode([String].self, forKey: .perFacet)
} catch DecodingError.typeMismatch {
perFacet = []
}
do {
multimedia = try container.decode([Multimedia].self, forKey: .multimedia)
} catch DecodingError.typeMismatch {
multimedia = nil
}
title = try container.decode(String.self, forKey: .title)
url = try container.decode(URL.self, forKey: .url)
}
}
struct Multimedia: Decodable {
let url: URL
let format, type, subtype, caption, copyright: String
let height, width: Int
}
let data = Data(jsonString.utf8)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let result = try decoder.decode(Article.self, from: data)
print(result)
} catch { print(error) }