在 Swift 4 中使用 Decodable 和 CodingKeys 解析 JSON
Posted
技术标签:
【中文标题】在 Swift 4 中使用 Decodable 和 CodingKeys 解析 JSON【英文标题】:Parsing JSON using Decodable and CodingKeys in Swift 4 【发布时间】:2017-10-18 22:40:18 【问题描述】:我正在尝试解析从 URL 检索的大型 JSON 字符串。我用来测试的 JSON 如下:
let json = """
"feed":
"title": "Harry Potter",
"test": "I dont want this value",
"results": [
"author": "JK Rowling",
"artworkURL": "A url",
"genres": [
"name": "Fantasy"
,
"name": "Scifi"
],
"name": "Goblet of Fire",
"releaseDate": "2000-07-08"
,
"author": "JK Rowling",
"artworkURL": "A url",
"genres": [
"name": "Fantasy"
,
"name": "Scifi"
],
"name": "Half Blood Prince",
"releaseDate": "2009-07-15"
]
""".data(using: .utf8)!
我有几个数据结构可以将数据放入:
struct Genre: Decodable
let name: String
struct Book: Decodable
let author: String
let artworkURL: URL
let genres: [Genre]
let name: String
let releaseDate: String
struct BookCollection
let title: String
let books: [Book]
enum CodingKeys: String, CodingKey
case feed
enum FeedKeys: String, CodingKey
case title, results
enum ResultKeys: String, CodingKey
case author, artworkURL, genres, name, releaseDate
enum GenreKeys: String, CodingKey
case name
extension BookCollection: Decodable
init(from decoder: Decoder) throws
let values = try decoder.container(keyedBy: CodingKeys.self)
let feed = try values.nestedContainer(keyedBy: FeedKeys.self,
forKey: .feed)
self.title = try feed.decode(String.self, forKey: .title)
self.books = try feed.decode([Track].self, forKey: .results)
然后我打印出这样的信息:
do
let response = try JSONDecoder().decode(BookCollection.self, from: json)
for book in response.books
print(book.genres)
catch
print(error)
它可以成功打印除类型之外的所有信息。这给了我一系列流派,但我做不到
book.genres.name
访问名称。我必须使用:
book.genres[0]
它只给了我第一个索引的结果。
有没有一种方法可以完善我的 BookCollection
扩展中的 JSON 解码,然后使用 book.genres.name
?
谢谢
【问题讨论】:
明确一点,book.genres.name
应该返回什么值?
@PauloMattos 它应该为每本书返回一个数组。第一本书是Fantasy
和Scifi
。
@DominicPilla 您可以将只读计算属性添加到您的 Book 结构 extension Book var allGenres: [String] return genres.map$0.name
并使用 print(book.allGenres)
【参考方案1】:
如果你真的需要那个额外的 name
属性,你可以在一个新的扩展中这样做:
extension Array where Element == Genre
var name: [String]
return self.map $0.name
这会将上述name
属性添加到每个 [Genre]
值,包括由您的Book
类型定义的值。请确保这确实是您所追求的(如果将此扩展名声明为 private
,那么它将在相应的 swift 文件中可用)。
【讨论】:
太棒了!那行得通。但是要确认在我的 BookCollection 扩展中没有办法做到这一点?我在那个扩展之外添加了这个,它工作得很好。 @DominicPilla 不,这是不可能的。你真的需要扩展[Genre]
来实现你的目标;)
我尝试为每个数据结构创建extensions
,但没有成功。你是说如果实施得当,它会起作用吗?
Swift 扩展机制相当灵活,应该可以!如果您有特定的案例,我建议您发布另一个问题。顺便说一句,如果有机会,请不要忘记将我的答案标记为已接受;)【参考方案2】:
为了消除使用许多枚举编码键和手动解码您的类型的需要,您可以将数据结构更改为映射 JSON 结构格式。注意下面的代码,不需要嵌套结构,你也可以把它并行。此代码已使用您的编码 JSON 数据进行测试
public struct HarryPotterFeed: Codable
public let feed: BookCollection
public struct BookCollection: Codable
public let title: String
public let books: [Book]
// map properties books to json's "results"
enum CodingKeys: String, CodingKey
case title // needs to come along
case books = "results"
public struct Book: Codable
public let author, name, artworkURL, releaseDate : String
public let genres: [Genre]
public struct Genre: Codable
public let name: String
// Decode JSON
do
let response = try JSONDecoder().decode(HarryPotterFeed.self, from: json)
for book in response.feed.books
for name in book.genres
print(name)
catch
print("PROBLEM DECODING JSON \(error)")
【讨论】:
感谢您的评论。 JSON 数据结构有多个不必要的键,并且会使我的数据结构充满无用的值!以上是关于在 Swift 4 中使用 Decodable 和 CodingKeys 解析 JSON的主要内容,如果未能解决你的问题,请参考以下文章
在 Swift 4 中使用 Decodable 解析 JSON
处理包含多种类型的 JSON 数组 - Swift 4 Decodable
使用 Swift 4 的 Decodable 解码 Void
Swift 4 Decodable - 没有与键 CodingKeys 关联的值 [重复]