Essentially I have the following JSON response I am decoding using the the following struct. What can be added to struct to make it work with a value that can either come through as a String or Int?
JSON Response:
[
{
"date": "2022-05-04",
"code": 122312,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]
Struct:
struct TestStruct: Decodable {
let date: String
let code: ??
let notes: String
}
CodePudding user response:
Assuming however the data received, the TestStruct
will have String
type for code
attribute the following snippet should decode it.
struct TestStruct: Decodable {
let date: String
let code: String?
let notes: String
enum CodingKeys: String, CodingKey {
case date, code, notes
}
// custom decoding
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.date = try container.decode(String.self, forKey: .date)
self.notes = try container.decode(String.self, forKey: .notes)
if let strCode = try container.decodeIfPresent(String.self, forKey: .code) {
self.code = strCode
} else if let intCode = try container.decodeIfPresent(Int.self, forKey: .code) {
self.code = String(intCode)
} else {
self.code = nil
}
}
}
CodePudding user response:
You will require a custom decoding implementation.
If the property can truly be either a number or a string, the easiest way is to use an enum type.
Additionally, if you want to decode a date object you'll need to pass in a date decoding strategy to your decoder.
Throw this in a playground:
let data = """
[
{
"date": "2022-05-04",
"code": 122312,
"notes": "Take keys"
},
{
"date": "2022-05-04",
"code": "Gate: 2312231",
"notes": "Take Box"
}
]
""".data(using: .utf8)!
enum Code: Decodable {
case int(Int)
case string(String)
}
struct Item: Decodable {
var date: Date
var code: Code
var notes: String
enum CodingKeys: String, CodingKey {
case date, code, notes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.date = try container.decode(Date.self, forKey: .date)
self.notes = try container.decode(String.self, forKey: .notes)
if let value = try? container.decode(Int.self, forKey: .code) {
self.code = .int(value)
} else if let value = try? container.decode(String.self, forKey: .code) {
self.code = .string(value)
} else {
let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unable to decode value for `code`")
throw DecodingError.typeMismatch(Code.self, context)
}
}
}
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
let items = try! decoder.decode([Item].self, from: data)
for item in items {
print("date: \(item.date.description)")
print("code: \(item.code)")
print("notes: \(item.notes)")
print()
}