Home > Blockchain >  How to parse Wikipedia JSON Dictionary with Swift?
How to parse Wikipedia JSON Dictionary with Swift?

Time:02-18

I'm new to the Wikipedia API and I have been trying to parse the image url from the API. The JSON I'm trying to parse is as follows:

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

JSON Result:

{"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"
      }
    }
  }
}

Below are the struct I created to parse the data. I can see the url when I print it to the console, but since "source" is nested under "thumbnail", which lives in the value of the [String:Page] dict pair, I can't figure out a way to access it. How do I parse data in a dictionary like this? Thank you for your help in advance.

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: $0.key, value: $0.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)
}

CodePudding user response:

Do you mean gathering all thumbnail sources?

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

CodePudding user response:

This works with your example perfectly:

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")]))
  • Related