Skip to content

Instantly share code, notes, and snippets.

@AliSoftware
Created January 24, 2019 14:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AliSoftware/70d5d83f47711598bfefd02f7b3ee555 to your computer and use it in GitHub Desktop.
Save AliSoftware/70d5d83f47711598bfefd02f7b3ee555 to your computer and use it in GitHub Desktop.
Techniques to decode Decodable types that are nested under intermediate keys in a JSON
import Foundation
let json = """
{
"data": {
"items": [
{ "name": "Alice" },
{ "name": "Bob" },
{ "name": "Claudia" }
]
}
}
""".data(using: .utf8)!
struct Item: Decodable, CustomStringConvertible {
let name: String
var description: String { return "<Item \(name)>" }
}
//: Solution 1: Create intermediate types just for decoding
struct DataWrapper: Decodable { let data: ItemsWrapper }
struct ItemsWrapper: Decodable { let items: [Item] }
let items = try JSONDecoder().decode(DataWrapper.self, from: json).data.items
print(items)
//: Solution 2: More generic
extension JSONDecoder {
private struct Wrapper: Decodable {
let container: KeyedDecodingContainer<AnyCodingKey>
struct AnyCodingKey: CodingKey {
let stringValue: String
let intValue: Int?
init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = Int(stringValue)
}
init?(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
init(from decoder: Decoder) throws {
self.container = try decoder.container(keyedBy: AnyCodingKey.self)
}
func nestedContainer<S: Sequence>(under keys: S) throws -> KeyedDecodingContainer<AnyCodingKey> where S.Element == String {
return try keys.reduce(self.container) { d, k in
return try d.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue: k)!)
}
}
}
func decode<T: Decodable>(_ type: T.Type, from data: Data, nestedIn keys: String...) throws -> T {
return try self.decode(Wrapper.self, from: data)
.nestedContainer(under: keys.dropLast())
.decode(T.self, forKey: Wrapper.AnyCodingKey(stringValue: keys.last!)!)
}
}
let items2 = try JSONDecoder().decode([Item].self, from: json, nestedIn: "data", "items")
print(items2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment