Я считаю, что ваш JSON несколько неверен, и я сделал некоторые предположения о том, как вы хотите, чтобы это работало, но вот как я бы подошел к этому.
Во-первых, вот JSON, который, я полагаю, вы имеете в виду(Я удалил лишние []
, которые синтаксически некорректны):
let json = Data("""
{
"stats": {
"total": {
"dagegen gestimmt": 196,
"nicht beteiligt": 41,
"dafür gestimmt": 435,
"enthalten": 37
},
"party_A": {
"dagegen gestimmt": 90,
"nicht beteiligt": 2
},
"party_B": {
"dafür gestimmt": 230,
"nicht beteiligt": 16
},
"party_C": {
"dagegen gestimmt": 1,
"nicht beteiligt": 1
},
"party_D": {
"dagegen gestimmt": 31,
"enthalten": 35,
"nicht beteiligt": 1
},
"party_E": {
"dagegen gestimmt": 64,
"nicht beteiligt": 5
}
}
}
""".utf8)
Нам нужен способ для декодирования одного PartyResult. Мы не можем использовать Decodable непосредственно здесь, потому что нам нужно передать имя, но это нормально. Мы можем сделать наш собственный init
, который принимает контейнер и имя. Я предполагаю, что вы хотите использовать значение по умолчанию 0 для пропущенных значений (вместо того, чтобы делать их необязательными).
struct PartyResult {
enum CodingKeys: String, CodingKey {
case dagegenGestimmt = "dagegen gestimmt"
case nichtBeteiligt = "nicht beteiligt"
case dafürGestimmt = "dafür gestimmt"
case enthalten
}
let name: String
let dagegenGestimmt: Int
let nichtBeteiligt: Int
let dafürGestimmt: Int
let enthalten: Int
init(name: String, container: KeyedDecodingContainer<CodingKeys>) throws {
func decode(_ key: CodingKeys) throws -> Int {
return try container.decodeIfPresent(Int.self, forKey: key) ?? 0
}
self.name = name
self.dagegenGestimmt = try decode(.dagegenGestimmt)
self.nichtBeteiligt = try decode(.nichtBeteiligt)
self.dafürGestimmt = try decode(.dafürGestimmt)
self.enthalten = try decode(.enthalten)
}
}
Как и во многих проблемах с ключом словаря - свойство>, нам понадобится какой-то видAnyKey, который может обрабатывать произвольные строки. Это простой, который обрабатывает только строки, а не целые числа.
struct AnyStringKey: CodingKey, Equatable {
var stringValue: String
init(stringValue: String) { self.stringValue = stringValue }
init(_ stringValue: String) { self.init(stringValue: stringValue) }
var intValue: Int?
init?(intValue: Int) { return nil }
}
С этим мы можем декодировать внешний stats
объект:
struct Stats: Decodable {
// This could be replaced with AnyStringKey, but just to be explicit.
enum CodingKeys: CodingKey { case stats }
let partyResults: [PartyResult]
init(from decoder: Decoder) throws {
// Get outer container
let outerContainer = try decoder.container(keyedBy: CodingKeys.self)
// Get inner "stats" container
let container = try outerContainer.nestedContainer(keyedBy: AnyStringKey.self,
forKey: .stats)
// Map each entry to a result
self.partyResults = try container.allKeys
.filter { $0 != AnyStringKey("total") } // Assuming you want to ignore the total key
.map { name in
try PartyResult(name: name.stringValue,
container: container.nestedContainer(keyedBy: PartyResult.CodingKeys.self,
forKey: name))
}
}
}
let results = try JSONDecoder().decode(Stats.self, from: json).partyResults
▿ 5 elements
▿ __lldb_expr_20.PartyResult
- name: "party_A"
- dagegenGestimmt: 90
- nichtBeteiligt: 2
- dafürGestimmt: 0
- enthalten: 0
▿ __lldb_expr_20.PartyResult
- name: "party_D"
- dagegenGestimmt: 31
- nichtBeteiligt: 1
- dafürGestimmt: 0
- enthalten: 35
▿ __lldb_expr_20.PartyResult
- name: "party_B"
- dagegenGestimmt: 0
- nichtBeteiligt: 16
- dafürGestimmt: 230
- enthalten: 0
▿ __lldb_expr_20.PartyResult
- name: "party_C"
- dagegenGestimmt: 1
- nichtBeteiligt: 1
- dafürGestimmt: 0
- enthalten: 0
▿ __lldb_expr_20.PartyResult
- name: "party_E"
- dagegenGestimmt: 64
- nichtBeteiligt: 5
- dafürGestimmt: 0
- enthalten: 0