2017-10-18 209 views
0

我想解析一个大的JSON字符串,我从URL中检索。我使用的测试JSON低于:在Swift中使用Decodable和CodingKeys解析JSON 4

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

谢谢

+0

要清楚,'book.genres.name'应该返回什么值? –

+0

@PauloMattos它应该为每本书返回一个数组。第一本书是“幻想”和“科幻”。 –

+0

@DominicPilla你可以添加一个只读的计算属性到你的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文件中可用)。

+0

太棒了!这工作。但要确认我的BookCollection扩展程序中没有办法做到这一点?我在该扩展之外添加了它,它工作得很好。 –

+0

@DominicPilla不,这是不可能的。你真的需要扩展'[流派]'来实现你的目标;) –

+0

我试图为每个数据结构创建'extensions',但是我没有成功。你是说如果实施正确,那么它会工作? –

0

为了消除使用许多枚举编码键和手动解码类型的需求,您可以更改数据结构以映射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)") 
} 
+0

谢谢您的评论。 JSON数据结构有多个不必要的键,并且会让我的数据结构充满无用的值! –