How to decode JSON object, which contains different keys?

I have json response like this and server returns different keys for "prices" and in sometimes "prices" may be false

{ "id": 194,
  "name": "Apple iPhone 11 128GB Black",
  "prices": {
        "100": "23500",
        "200": "23300"
    }
{ "id": 195,
  "name": "Samsung Galaxy A12",
  "prices": {
        "500": "23500",
        "200": "23300"
    }

When keys for prices were only "100" and "200", I decoded like this but it now gives error:

struct ProductDetail: Decodable, Hashable {
    
    let name: String
    let prices: PricesResponse
struct PricesResponse: Decodable, Hashable {
  
    let firstPrice: String
    let secondPrice: String

    enum CodingKeys: String, CodingKey, Decodable {

        case firstPrice = "100"
        case secondPrice = "200"
    

Could you please say how can I decode json in this case?

1 Like

One idea could be to decode a dictionary of the prices?

let priceResponse: [String: String]

That would be a good choice if the keys for the prices are completely dynamic.

1 Like

you should be able to use swift-json for this:

guard   case .object(let items) = 
        try Grammar.parse(utf8, as: JSON.Rule<String.Index>.Root.self), 
        case .object(let prices)? = items["prices"]
else 
{
    ...
}

which will give you a [String: JSON] dictionary. you will then have to check that each JSON value is a JSON.string(_:) case.

1 Like

Codable supports this, actually. CodingKey doesn't need to be an enum, it can be a struct. Then in your init(from:), you can loop over allKeys to read out your keys. For known keys, you can define static properties.

struct WhateverObject: Codable {
    var myProperty: String // properties...

    struct CodingKeys: CodingKey, Hashable {
        var stringValue: String

        static let myProperty = CodingKeys(stringValue: "myProperty")
        
        init(stringValue: String) {
            self.stringValue = stringValue
        }
        
        // Unused, only for int-backed Codables.
        var intValue: Int? { return nil }
        init?(intValue: Int) { return nil }
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        myProperty = container.decode(String.self, forKey: .myProperty)

        let dynamicKeys = container.allKeys
        for key in dynamicKeys {
            // do whatever you want to do with the keys.
        }
    }
}
3 Likes