0

I'm building an app that queries wikidata using json. It works so far, but the issue I'm running into is the response I get from wikidata isn't the actual data, but an identifier. Then, to convert that identifier, I need to send it to another url and receive another json response, but the issue I'm running into is the the key value isn't something I know until I get the first json response in. So, lets say my key is Q1169621. When I run it through the api, I get this response: response from wikipedia api

I'm using codable and JSONDecoder, but I don't know how to tell the decoder the value of the key in entities is Q1169621 to get at the value I want ("Jim Lauderdale")...some of my code is below, I have structs to define the data of the response, but how do I replace the key value in my struct with the one defined from the previous json that is decoded?

struct InfoFromWikiConverted: Codable {
    
    let entities: Locator //this is the value I need to set before parsing the json
    
}

struct Locator: Codable {
    
    let labels: Labels //how do I link this to the struct above?
}

struct Labels: Codable {
    
    let en: EN
}

struct EN: Codable {
    
    let value: String
}
Benjamin B.
  • 521
  • 7
  • 15

1 Answers1

1

The simplest approach would be to decode entities as [String: Locator]:

struct InfoFromWikiConverted: Decodable {
   let entities: [String: Locator]
}

Of course, if you want your model to just be a single Locator (which means, potentially ignoring multiple keys under entities), then you'd need to manually decode it.

You'd need to create a type to represent any string Coding Key and implement init(from:):

struct InfoFromWikiConverted: Decodable {
   let entities: Locator
   
   struct CodingKeys: CodingKey {
      var stringValue: String
      var intValue: Int? = nil
        
      init(stringValue: String) { self.stringValue = stringValue }
      init?(intValue: Int) { return nil }
   }

   init(from decoder: Decoder) throws {
      let container = try decoder.container(keyedBy: CodingKeys.self)

      // get the first key (ignore the rest), and throw if there are none
      guard let key = container.allKeys.first else {
         throw DecodingError.dataCorrupted(
            .init(codingPath: container.codingPath, 
                  debugDescription: "Expected a non-empty object"))
      }

      let entities = try container.decode(Locator.self, forKey: key)
   }
}

Note that because you aren't retaining the ID, you can't encode it back to the same form, so I only conformed to Decodable instead of Codable

New Dev
  • 48,427
  • 12
  • 87
  • 129