Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Decode JSON with dynamic key
import Foundation
let clientJSON = """
{
"clients": [
{
"name": "John",
"amount": 1000
},
{
"name": "Tony",
"weirdAmount": 2000
},
{
"name": "Jack",
"specialAmount": 3000
},
{
"name": "Stark",
"totalAmount": 4000
}
]
}
""".data(using: .utf8)!
struct DynamicKey: CodingKey {
var stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int?
init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
init(string: String) { self.stringValue = string }
}
protocol SpecialCaseConvertible: CaseIterable, RawRepresentable where RawValue == String {
static var targetCase: String { get }
}
struct Clients: Decodable {
let clients: [Client]
}
struct Client: Decodable {
let name: String
let amount: Double
enum CodingKeys: String, CodingKey {
case name
case amount
}
enum SpecialCase: String, SpecialCaseConvertible {
case totalAmount
case weirdAmount
case specialAmount
static var targetCase: String { CodingKeys.amount.rawValue }
}
}
func convert(transformer: @escaping (CodingKey?) -> CodingKey,
when predicate: @escaping (CodingKey) -> Bool) -> ([CodingKey]) -> CodingKey {
return { keys in
guard let last = keys.last else { return transformer(nil) }
return predicate(last) ? transformer(last) : last
}
}
func convert<T: SpecialCaseConvertible>(_ type: T.Type) -> ([CodingKey]) -> CodingKey {
let inputs = Set(T.allCases.map { $0.rawValue })
return convert(transformer: { _ in DynamicKey(string: T.targetCase) },
when: { inputs.contains($0.stringValue) })
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom(convert(Client.SpecialCase.self))
let result = try decoder.decode(Clients.self, from: clientJSON)
print(result.clients.map { $0.amount } )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment