我正在使用《纽约时报》 API并收到消息
typeMismatch(Swift.Array,Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:“ results”,intValue:nil), _JSONKey(stringValue:“ Index 3”,intValue:3),CodingKeys(stringValue:“ multimedia”,intValue:nil)],debugDescription:“预期解码 数组,但找到了一个字符串/数据。”,底层错误:nil))
当JSON的多媒体部分是字符串而不是数组时,就会发生这种情况:类似于此SO问题:
Swift Codable expected to decode Dictionary<String, Any>but found a string/data instead
所以我决定举一个最小的例子。
附文章
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": ""
}
"""
即使我将对象中的属性设置为可选,也使用了self。,它们都使用了encoder.decode方法。
如何获取第二个JSON字符串进行解码?
答案 0 :(得分:1)
解决方案
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
}
编辑
尝试一下
{{1}}
参考号:https://medium.com/swiftly-swift/swift-4-decodable-beyond-the-basics-990cc48b7375
答案 1 :(得分:1)
嗯,这种不一致应该由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?
}
答案 2 :(得分:1)
一种解决方案是手动解码所有密钥,其值可以是不同的类型。
在此示例中,multimedia
是可选的([Multimedia]
或nil
),而perFacet
是非可选的[String]
,如果该值为空,则为空字符串。
所有结构成员都是常量(let
),并添加了convertFromSnakeCase
策略以摆脱 snake_cased 名称
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) }