Last active
July 30, 2021 17:56
-
-
Save TaLinh/06668c74e6ad68beef1d231759db5de1 to your computer and use it in GitHub Desktop.
Decode JSON with dynamic key
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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