I have been struggling to develop a code to decode multiple different JSON strings into a combined data structure (struct).
As you can see in the code below, the two JSON strings correctInput
and faultyInput
, contain the same values but the key has a different name.
Is there a way to decode such different strings (2 or more) into a common Codable struct?
Thank you!
import Foundation
struct Fruit: Codable, Equatable {
var apple: String
var banana: String
var pineapple: String
}
let correctInput = """
{
"apple": "Akane",
"banana": "Bria",
"pineapple": "Sarawak"
}
""".data(using: .utf8)!
let faultyInput = """
{
"appl": "Akane",
"nana": "Bria",
"pine": "Sarawak"
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let correctFruit = try? decoder.decode(Fruit.self, from: correctInput)
let faultyFruit = try? decoder.decode(Fruit.self, from: faultyInput)
print(correctFruit)
print(faultyFruit)
Here is a solution where we have a separate struct for the faulty data with a built in mapping function
struct FaultyFruit: Codable, Equatable {
var appl: String
var nana: String
var pine: String
func asFruit() -> Fruit {
Fruit(apple: appl, banana: nana, pineapple: pine)
}
}
And then we decode like this
var fruit: Fruit?
do {
fruit = try? decoder.decode(Fruit.self, from: faultyInput)
if fruit == nil {
let faultyFruit = try decoder.decode(FaultyFruit.self, from: faultyInput)
fruit = faultyFruit.asFruit()
}
} catch {
print(error)
}
And here is a solution where a custom init(from:)
is used and different keys/property names are tried.
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let values = try container.decode([String: String].self)
apple = values["apple"] ?? values["appl"] ?? values["a"] ?? ""
banana = values["banana"] ?? values["nana"] ?? ""
pineapple = values["pineapple"] ?? values["pine"] ?? ""
}
Quietly assigning "" to a property might not be the correct way so you could add a check for nil and throw an error if no value could be parsed for a property.