我正在尝试根据编码数据的内容解码特定的类。
class Vehicle: Codable {
enum Kind: Int, Codable {
case car = 0, motorcycle = 1
}
let brand: String
let numberOfWheels: Int
}
class Car: Vehicle {}
class MotorCycle: Vehicle {}
如您所见,我有一个通用的 Vehicle
类型,用于对车辆进行编码和解码。这对于如下所示的基本解码效果很好。
let car = "{\"kind\": 0, \"brand\": \"Ford\", \"number_of_wheels\": 4}".data(using: .utf8)!
let motorCycle = "{\"kind\": 1, \"brand\": \"Yamaha\", \"number_of_wheels\": 2}".data(using: .utf8)!
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
// Outputs a Project.Car
let ford = try! decoder.decode(Car.self, from: car)
// Outputs a Project.MotorCycle
let yamaha = try! decoder.decode(MotorCycle.self, from: motorCycle)
但是如果我想解码一组车辆,但将它们解码为特定类型怎么办?
let combined = "[{\"kind\": 0, \"brand\": \"Ford\", \"number_of_wheels\": 4}, {\"kind\": 1, \"brand\": \"Yamaha\", \"number_of_wheels\": 2}]".data(using: .utf8)!
// Outputs [Project.Vehicle, Project.Vehicle]
print(try! decoder.decode([Vehicle].self, from: combined))
如何通过使用 JSON 数据中的 kind 属性让解码器输出车辆数组,但类型化的车辆。按照示例[Project.Car, Project.MotorCycle]
(如果可能的话)。
最佳答案
这是一个不使用Codable
的替代解决方案。我还做了一些更改并引入了协议(protocol)而不是父类(super class)。
protocol Vehicle: CustomStringConvertible {
var brand: String { get set }
var numberOfWheels: Int { get set }
}
extension Vehicle {
var description: String {
"\(brand), wheels: \(numberOfWheels), type: \(type(of:self))"
}
}
不太重要,但我将类型从类更改为结构
struct Car: Vehicle {
var brand: String
var numberOfWheels: Int
}
struct MotorCycle: Vehicle {
var brand: String
var numberOfWheels: Int
}
然后使用 JSONSerialization
进行解码,分两步将 json 转换为 Vehicle
数组,然后使用 reduce(into:)
创建对象
do {
if let array = try JSONSerialization.jsonObject(with: combined) as? [[String: Any]] {
let vehicles = array.reduce(into: [Vehicle]()) {
if let kindValue = $1["kind"] as? Int,
let kind = VehicleKind(rawValue: kindValue),
let brand = $1["brand"] as? String,
let numberOfWheels = $1["number_of_wheels"] as? Int {
switch kind {
case .car:
$0.append(Car(brand: brand, numberOfWheels: numberOfWheels))
case .motorcycle:
$0.append(MotorCycle(brand: brand, numberOfWheels: numberOfWheels))
}
}
}
for vehicle in vehicles {
print(vehicle)
}
}
} catch {
print(error)
}
以上代码输出:
Ford, wheels: 4, type: Car
Yamaha, wheels: 2, type: MotorCycle
更新。可编码版本
我还通过引入一种用于解码的单独类型,设法提出了可编码解决方案。设置与以前相同,有一个协议(protocol)和两个结构。
然后我介绍了一种用于解码的特定类型(当然也可以扩展用于编码),它实现了自定义 init(from:)
struct JsonVehicle: Decodable {
let vehicle: Vehicle
enum CodingKeys: String, CodingKey {
case kind
case brand
case numberOfWheels
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let kind = try container.decode(VehicleKind.self, forKey: .kind)
let brand = try container.decode(String.self, forKey: .brand)
let wheels = try container.decode(Int.self, forKey: .numberOfWheels)
switch kind {
case .car:
vehicle = Car(brand: brand, numberOfWheels: wheels)
case .motorcycle:
vehicle = MotorCycle(brand: brand, numberOfWheels: wheels)
}
}
}
最终结果还是通过两步实现的
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([JsonVehicle].self, from: combined)
let vehicles = result.map(\.vehicle)
} catch {
print(error)
}
关于json - 根据编码值 Swift 解码类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65521457/