Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active June 9, 2020 18:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rnapier/eb3b8628ef5e8a15f3794fa70ae96f3a to your computer and use it in GitHub Desktop.
Save rnapier/eb3b8628ef5e8a15f3794fa70ae96f3a to your computer and use it in GitHub Desktop.
// From https://stackoverflow.com/questions/54129682/use-swift-codable-to-decode-json-with-values-as-keys
import Foundation
let json = Data("""
{
"7E7-M001" : {
"Drawer1" : {
"101" : {
"Partnumber" : "F101"
},
"102" : {
"Partnumber" : "F121"
}
},
"Drawer2" : {
"101" : {
"Partnumber" : "F101"
},
"102" : {
"Partnumber" : "F121"
}
}
},
"7E7-M002": {
"Drawer1": {
"201": {
"Partnumber": "F201"
},
"202": {
"Partnumber": "F221"
}
}
}
}
""".utf8)
protocol TitleDecodable: Decodable {
associatedtype Element: Decodable
init(title: String, elements: [Element])
}
extension TitleDecodable {
init(from decoder: Decoder) throws {
self.init(title: try decoder.currentTitle(),
elements: try decoder.decodeTitledElements(Element.self))
}
}
struct Tool: Decodable {
let title: String
let partNumber: String
enum CodingKeys: String, CodingKey {
case partNumber = "Partnumber"
}
init(from decoder: Decoder) throws {
self.title = try decoder.currentTitle()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.partNumber = try container.decode(String.self, forKey: .partNumber)
}
}
struct Drawer: TitleDecodable {
let title: String
let tools: [Tool]
init(title: String, elements: [Tool]) {
self.title = title
self.tools = elements
}
}
struct Container: TitleDecodable {
let title: String
let drawers: [Drawer]
init(title: String, elements: [Drawer]) {
self.title = title
self.drawers = elements
}
}
struct TitleKey: CodingKey {
let stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
extension Decoder {
func currentTitle() throws -> String {
guard let titleKey = codingPath.last as? TitleKey else {
throw DecodingError.dataCorrupted(.init(codingPath: codingPath,
debugDescription: "Not in titled container"))
}
return titleKey.stringValue
}
func decodeTitledElements<Element: Decodable>(_ type: Element.Type) throws -> [Element] {
let titles = try container(keyedBy: TitleKey.self)
return try titles.allKeys.map { title in
return try titles.decode(Element.self, forKey: title)
}
}
}
struct Containers: Decodable {
let containers: [Container]
init(from decoder: Decoder) throws {
self.containers = try decoder.decodeTitledElements(Container.self)
}
}
let containers = try JSONDecoder().decode(Containers.self, from: json)
print(containers.containers)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment