Swift json dynamic key parsing for json

499 views Asked by At

I have json response where only just one key name change rest is same and want to parse without duplicating same struct again.

"attributes": {
  "symbol":"EUR",
  "name":"Euro",
  "precision":2,             
}
    
"attributes":{
  "symbol":"EUR",
  "name":"Euro",
  "precision_for_fiat_price":2,  
}

How can handle this precision key dynamically in json parsing

2

There are 2 answers

0
Sweeper On BEST ANSWER

You can use a custom keyDecodingStrategy.

Essentially, you write some logic that checks whether the current coding key path matches some criteria, and if it does, map that key to the precision key.

For example:

struct Root : Codable {
    let attributes: Attributes
}

struct Attributes : Codable {
    let symbol: String
    let name: String
    let precision: Int
    
    enum CodingKeys: CodingKey {
        case symbol
        case name
        case precision
    }
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom({
    keys in

    // This will decode every precision_for_fiat_price key in the json as "precision".
    // You might not want this. 
    // Make this criteria stricter if you need to. An example is shown below
    if keys.last?.stringValue == "precision_for_fiat_price" {
        return Attributes.CodingKeys.precision
    }

    // this will only decode those precision_for_fiat_price that have "attribute" as their parent as "precision"
//    if stringPath.suffix(2) == ["attributes", "precision_for_fiat_price"] {
//        return Attributes.CodingKeys.precision
//    }
    return keys.last!
})
let json = """
{
    "attributes":{
                  "symbol":"EUR",
                  "name":"Euro",
                  "precision_for_fiat_price":2
                  
     }
}
""".data(using: .utf8)!
let decoded = try decoder.decode(Root.self, from: json)
1
aturan23 On

If you want to just decode json model like this:

let json = """
{
  "attributes": {
     "symbol":"EUR",
     "name":"Euro",
     "precision_for_fiat_price":2 // or "precision": 2
  }
}
"""

You can create Decodable struct:

struct WrapperModel: Decodable { // any model
    var attributes: Attributes
}

struct Attributes : Decodable {
    let symbol: String
    let name: String
    var precision: Int = 0
    
    enum CodingKeys: String, CodingKey, CaseIterable {
        case symbol
        case name
        case precision
        case precisionAnother = "precision_for_fiat_price"
        // you can write any types of key here
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        symbol = try container.decode(String.self, forKey: .symbol)
        name = try container.decode(String.self, forKey: .name)
        if let precisionValue = try container.decodeIfPresent(Int.self, forKey: .precision) {
            precision = precisionValue
        }
        if let precisionValue = try container.decodeIfPresent(Int.self, forKey: .precisionAnother) {
            precision = precisionValue
        }
    }
}

You can test it with:

let jsonData = Data(json.utf8)

let decoder = JSONDecoder()

do {
    let attributes = try decoder.decode(WrapperModel.self, from: jsonData)
    print(attributes)
} catch {
    print(error.localizedDescription)
}