2017-10-18 8 views
0

Ich versuche, eine große JSON-Zeichenfolge zu analysieren, die ich von einer URL abrufe. Die JSON ich zu Test bin mit unter:Analysieren von JSON mit Decodable und CodingKeys in Swift 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)! 

ich ein paar Daten structs haben die Daten in platzieren:

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) 
    } 
} 

ich dann die Informationen Druck aus wie so bin:

do { 
    let response = try JSONDecoder().decode(BookCollection.self, from: json) 
    for book in response.books { 
     print(book.genres) 
    } 
} catch { 
    print(error) 
} 

Es ist erfolgreich beim Ausdrucken aller Informationen mit Ausnahme der Genres. Dies gibt mir eine Reihe von Genres, aber ich kann nicht book.genres.name Zugriff auf den Namen. Ich muss verwenden: book.genres[0] und es gibt mir Ergebnisse für nur den ersten Index.

Gibt es eine Möglichkeit, meine JSON-Decodierung in meiner BookCollection Erweiterung zu perfektionieren, um dann book.genres.name zu verwenden?

Danke

+0

Um klar zu sein, welchen Wert "book.genres.name" sollte zurückgeben? –

+0

@PauloMattos sollte es ein Array für jedes Buch zurückgeben. Für das erste Buch wäre es "Fantasy" und "Scifi". –

+0

@DominicPilla Sie können eine schreibgeschützte berechnete Eigenschaft zu Ihrer Buch-Struktur hinzufügen 'Erweiterung Buch { var allGenres: [String] { Rückkehr genres.map {$ 0. Name} } }' und verwenden Sie 'print (book.allGenres) ' –

Antwort

1

Wenn Sie wirklich, dass zusätzliche name Eigenschaft benötigen, können Sie so in einer neuen Erweiterung können:

extension Array where Element == Genre { 
    var name: [String] { 
     return self.map { $0.name } 
    } 
} 

Das die zuvor erwähnte dort jeder[Genre] Wert name Eigenschaft fügt hinzu: einschließlich des von Ihrem Typ Book definierten Typs. Seien Sie einfach sicher, dass Sie wirklich danach suchen (wenn Sie diese Erweiterung als private deklarieren, als sie in der entsprechenden swift-Datei verfügbar ist).

+0

Ehrfürchtig! Das hat funktioniert. Aber um dies zu bestätigen, gibt es keine Möglichkeit, dies innerhalb meiner BookCollection-Erweiterung zu tun. Ich fügte das außerhalb dieser Erweiterung hinzu und es funktionierte gut. –

+0

@DominicPilla Nein, das ist nicht möglich. Sie müssen wirklich '[Genre]' erweitern, um Ihr Ziel zu erreichen;) –

+0

Ich habe versucht, 'Erweiterungen' für jede meiner Datenstrukturen zu machen, aber ich war nicht erfolgreich. Wollen Sie sagen, wenn es korrekt implementiert ist, dann würde es funktionieren? –

0

Um die Notwendigkeit zu vermeiden, viele enum codingKeys zu verwenden und Ihren Typ manuell zu decodieren, können Sie Ihre Datenstruktur ändern, um das JSON-Strukturformat abzubilden. Beachten Sie, dass der folgende Code nicht notwendig ist, um die Strukturen zu verschachteln, Sie können es auch parallel setzen. Dieser Code wurde mit Ihren codierten JSON-Daten getestet

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

Vielen Dank für Ihren Kommentar. Die JSON-Datenstruktur hatte mehrere Schlüssel, die nicht notwendig waren, und hätte meine Datenstrukturen mit nutzlosen Werten gefüllt! –

Verwandte Themen