2017-10-18 16 views
0

J'essaye d'analyser une grande chaîne JSON que je récupère à partir d'une URL. JSON J'utilise le test est inférieur à:Analyse de JSON à l'aide de Decodable et CodingKeys dans 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)! 

J'ai un struct de données couple pour placer les données dans:

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

Je suis ensuite l'impression de l'information comme ceci:

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

Il réussit à imprimer toutes les informations sauf les genres. Cela me donne un tableau de genres, mais je ne peux pas faire book.genres.name pour accéder au nom. Je dois utiliser: book.genres[0] et il me donne des résultats pour seulement le premier index.

Y at-il un moyen que je pourrais perfectionner mon décodage JSON dans mon extension BookCollection pour ensuite utiliser book.genres.name?

Merci

+0

Pour être clair, quelle valeur 'book.genres.name' devrait renvoyer? –

+0

@PauloMattos il devrait retourner un tableau pour chaque livre. Pour le premier livre, ce serait "Fantasy" et "Scifi". –

+0

@DominicPilla vous pouvez ajouter une propriété calculée en lecture seule à votre livre Book struct { var allGenres: [String] { return genres.map {$ 0.name} } } 'et d'utiliser' print (book.allGenres) ' –

Répondre

1

Si vous avez vraiment besoin de ce supplément propriété name, vous pouvez le faire dans une nouvelle extension:

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

Cela ajoute la propriété name mentionnée ci-dessus à tous les[Genre] valeur là-bas, y compris celui défini par votre type Book. Assurez-vous simplement que c'est vraiment ce que vous recherchez (si vous déclarez cette extension comme private, elle sera disponible dans le fichier swift correspondant).

+0

Génial! Ça a marché. Mais pour confirmer il n'y a aucun moyen de le faire dans mon extension BookCollection? J'ai ajouté ceci en dehors de cette extension et cela a très bien fonctionné. –

+0

@DominicPilla Non, ce n'est pas possible. Vous avez vraiment besoin d'étendre '[Genre]' pour atteindre votre objectif;) –

+0

J'ai essayé de faire des extensions pour chacune de mes structures de données, mais je n'ai pas réussi. Êtes-vous en train de dire que si elle est mise en œuvre correctement, cela fonctionnera? –

0

Pour éliminer le besoin d'utiliser de nombreux codes de codage enum et de décoder votre type manuellement, vous pouvez modifier votre structure de données pour mapper le format de structure JSON. Notez que le code ci-dessous, il n'est pas nécessaire d'imbriquer les structures, vous pouvez également le mettre en parallèle. Ce code est testé avec vos données JSON encodées

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

Merci pour votre commentaire. La structure de données JSON avait plusieurs clés qui n'étaient pas nécessaires et aurait rempli mes structures de données avec des valeurs inutiles! –