繁体   English   中英

在Swift 4中使用Decodable和CodingKeys解析JSON

[英]Parsing JSON using Decodable and CodingKeys in Swift 4

我正在尝试解析从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

谢谢

如果您确实需要该多余的name属性,则可以在新的扩展名中进行:

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

这会将上述name属性添加到其中的每个 [Genre]值中,包括您的Book类型定义的值。 只要确保这确实是您要的(如果将此扩展名声明为private ,那么它将在相应的swift文件中提供)。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM