如何用 Swift 解析维基百科 JSON 字典?

How to parse Wikipedia JSON Dictionary with Swift?

我是维基百科的新手 API,我一直在尝试从 API 解析图像 url。我试图解析的 JSON 如下:

API: https://en.wikipedia.org/w/api.php?action=query&titles=tokyo&prop=pageimages&format=json

JSON 结果:

{"batchcomplete": "",
  "query": {
    "normalized": [
      {
        "from": "tokyo",
        "to": "Tokyo"
      }
    ],
    "pages": {
      "30057": {
        "pageid": 30057,
        "ns": 0,
        "title": "Tokyo",
        "thumbnail": {
          "source": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
          "width": 50,
          "height": 27
        },
        "pageimage": "Skyscrapers_of_Shinjuku_2009_January.jpg"
      }
    }
  }
}

下面是我创建的用于解析数据的结构。当我将它打印到控制台时,我可以看到 url,但是由于“source”嵌套在“thumbnail”下,它存在于 [String:Page] dict 对的值中,我无法弄清楚一种访问它的方法。我如何解析像这样的字典中的数据?提前感谢您的帮助。

struct WikiAPIResults: Codable {
  let batchcomplete: String?
  let query: Query?
}

struct Query: Codable {
  let normalized: [Normalized]?
  let pages: [String:Pages]? // <- I can get to here
}

struct Normalized: Codable {
  let from, to: String? 
}

struct Pages: Codable {
  let pageid, ns: Int?
  let title: String?
  let thumbnail: Thumbnail?
  let pageimage: String?
}

struct Thumbnail: Codable {
  let source: String? // <- But I want to grab this
  let width, height: Int?
}

func fetchImageFromWikipedia(imageKeyword: String, completion: @escaping (WikiAPIResults) -> Void) {
    var urlComponents = URLComponents(string: "https://en.wikipedia.org/w/api.php?")!
    urlComponents.queryItems = [
    "action": "query",
    "titles": imageKeyword,
    "prop": "pageimages",
    "format": "json"].map { URLQueryItem(name: [=13=].key, value: [=13=].value) }
    
    let task = URLSession.shared.dataTask(with: urlComponents.url!) { data, response, error in
        let jsonDecoder = JSONDecoder()
        if let data = data,
           let result = try? jsonDecoder.decode(WikiAPIResults.self, from: data) {
            completion(result)
        }
    }
    
    task.resume()
}

fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
    print(result.query?.pages?.values)
}

您是说收集所有缩略图来源吗?

fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
    let pages = result.query?.pages?.compactMap { [=10=].value } ?? []
    let thumbnailSources = pages.compactMap { [=10=].thumbnail?.source }
    print(thumbnailSources)
}

这非常适合您的示例:

import Foundation

struct Response: Decodable {

    struct Query: Decodable {

        struct NormalizedQuery: Decodable {

            let from: String
            let to: String

        }

        struct Page: Decodable {

            struct Thumbnail: Decodable {

                let source: URL
                let width: Int
                let height: Int

            }

            let id: Int
            let ns: Int
            let title: String
            let thumbnail: Thumbnail
            let pageImage: String

            private enum CodingKeys: String, CodingKey {

                case id = "pageid"
                case ns
                case title
                case thumbnail
                case pageImage = "pageimage"

            }

        }

        let normalized: [NormalizedQuery]
        let pages: [String : Page]

    }

    let batchComplete: String
    let query: Query

    private enum CodingKeys: String, CodingKey {

        case batchComplete = "batchcomplete"
        case query

    }

}

let responseString = """
{
    "batchcomplete" : "",
    "query" : {
        "normalized" : [
            {
                "from" : "tokyo",
                "to" : "Tokyo"
            }
        ],
        "pages" : {
            "30057" : {
                "pageid" : 30057,
                "ns" : 0,
                "title" : "Tokyo",
                "thumbnail" : {
                    "source" : "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
                    "width" : 50,
                    "height" : 27
                },
                "pageimage" : "Skyscrapers_of_Shinjuku_2009_January.jpg"
            }
        }
    }
}
"""
let responseData = responseString.data(using: .utf8)!

let response = try! JSONDecoder().decode(Response.self, from: responseData)
print(response)
// Response(batchComplete: "", query: __lldb_expr_18.Response.Query(normalized: [__lldb_expr_18.Response.Query.NormalizedQuery(from: "tokyo", to: "Tokyo")], pages: ["30057": __lldb_expr_18.Response.Query.Page(id: 30057, ns: 0, title: "Tokyo", thumbnail: __lldb_expr_18.Response.Query.Page.Thumbnail(source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg, width: 50, height: 27), pageImage: "Skyscrapers_of_Shinjuku_2009_January.jpg")]))