2017-10-18 145 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數據結構有多個不必要的鍵,並且會讓我的數據結構充滿無用的值! –