我在这里做一个假设——“Swift 4 Decodable 解码 JSON 的方式”是指调用 try JSONDecoder().decode([Room].self, from: jsonData)。
如果是这种情况,那么据我所知,您很不走运,因为 JSONDecoder 将遍历其解析的 JSON 对象并在每个对象上调用初始化程序 Room(from: Decoder)。即使您要创建自己的初始化程序,它也无法知道其他 JSON 对象包含什么。
解决此问题的一种方法是创建一个反映每个 JSON 对象属性的中间 Decodable 结构,然后通过这些结构的数组创建您的 Rooms。
这是一个示例,可以作为 Xcode 游乐场使用:
import UIKit
struct Room {
let name: String
var devices: [Device]
fileprivate struct DeviceInRoom: Decodable {
let name: String
let room: String
}
static func rooms(from data: Data) -> [Room]? {
return (try? JSONDecoder().decode([DeviceInRoom].self, from: data))?.rooms()
}
}
struct Device {
let name: String
}
fileprivate extension Array where Element == Room.DeviceInRoom {
func rooms() -> [Room] {
var rooms = [Room]()
self.forEach { deviceInRoom in
if let index = rooms.index(where: { $0.name == deviceInRoom.room }) {
rooms[index].devices.append(Device(name: deviceInRoom.name))
} else {
rooms.append(Room(name: deviceInRoom.room, devices: [Device(name: deviceInRoom.name)]))
}
}
return rooms
}
}
let json = """
[
{
"name": "TV",
"room": "Living Room"
},
{
"name": "LightBulb 1",
"room": "Living Room"
}
]
"""
if let data = json.data(using: .utf8),
let rooms = Room.rooms(from: data) {
print(rooms)
}
或者 - 也许是一种更 Swift4'y 的方式:
import UIKit
struct Room {
let name: String
var devices: [Device]
}
struct Device {
let name: String
}
struct RoomContainer: Decodable {
let rooms: [Room]
private enum CodingKeys: String, CodingKey {
case name
case room
}
init(from decoder: Decoder) throws {
var rooms = [Room]()
var objects = try decoder.unkeyedContainer()
while objects.isAtEnd == false {
let container = try objects.nestedContainer(keyedBy: CodingKeys.self)
let deviceName = try container.decode(String.self, forKey: .name)
let roomName = try container.decode(String.self, forKey: .room)
if let index = rooms.index(where: { $0.name == roomName }) {
rooms[index].devices.append(Device(name: deviceName))
} else {
rooms.append(Room(name: roomName, devices: [Device(name: deviceName)]))
}
}
self.rooms = rooms
}
}
let json = """
[
{
"name": "TV",
"room": "Living Room"
},
{
"name": "LightBulb 1",
"room": "Living Room"
}
]
"""
if let data = json.data(using: .utf8),
let rooms = (try? JSONDecoder().decode(RoomContainer.self, from: data))?.rooms {
print(rooms)
}
注意 - 我在上面的代码中使用了几次try?。显然你应该正确处理错误 - JSONDecoder 会给你很好的,具体的错误取决于哪里出了问题! :)