根据编码值 Swift 解码 class
Decoding a class based on encoded value Swift
我正在尝试根据编码数据的内容解码特定的 class。
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 数据中的种类 属性 让解码器输出车辆数组,但类型为车辆。根据示例 [Project.Car, Project.MotorCycle]
如果可能的话。
您没有 class 中定义的种类 属性。
另外,Car和MotorCycle没有区别
我在操场上测试了这个,似乎做你想做的事。
class Vehicle: Codable {
enum Kind: Int, Codable {
case car = 0, motorcycle = 1
}
let kind: Kind
let brand: String
let numberOfWheels: Int
}
class Car: Vehicle {}
class MotorCycle: 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)
//But what if I want to decode an array of vehicles, but have them decoded as a specific type?
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]
let allVeh = try! decoder.decode([Vehicle].self, from: combined)
for veh in allVeh where veh.kind == Vehicle.Kind.motorcycle { // } is MotorCycle {
print(veh.brand, type(of: veh))
}
它给出:
雅马哈车辆
你也可以这样写
let allMotorcycles = allVeh.filter {[=11=].kind == Vehicle.Kind.motorcycle}
for moto in allMotorcycles {
print(moto.brand, type(of: moto))
}
这是一个不使用 Codable
的替代解决方案。我还做了一些更改并引入了一个协议而不是超级 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))"
}
}
不是那么重要,但我将类型从 class 更改为 struct
struct Car: Vehicle {
var brand: String
var numberOfWheels: Int
}
struct MotorCycle: Vehicle {
var brand: String
var numberOfWheels: Int
}
然后分两步将 json 转换为 Vehicle
数组,使用 JSONSerialization
进行解码,然后 reduce(into:)
创建对象
do {
if let array = try JSONSerialization.jsonObject(with: combined) as? [[String: Any]] {
let vehicles = array.reduce(into: [Vehicle]()) {
if let kindValue = ["kind"] as? Int,
let kind = VehicleKind(rawValue: kindValue),
let brand = ["brand"] as? String,
let numberOfWheels = ["number_of_wheels"] as? Int {
switch kind {
case .car:
[=12=].append(Car(brand: brand, numberOfWheels: numberOfWheels))
case .motorcycle:
[=12=].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
更新。可编码版本
我还通过引入一种用于解码的单独类型来设法提出一个 Codable 解决方案。设置与之前相同,有一个协议和两个结构。
然后我介绍一个特定的解码类型(但它当然也可以扩展为编码)实现自定义 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)
}
}
}
同样,最终结果是通过 2 个步骤实现的
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([JsonVehicle].self, from: combined)
let vehicles = result.map(\.vehicle)
} catch {
print(error)
}
我正在尝试根据编码数据的内容解码特定的 class。
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 数据中的种类 属性 让解码器输出车辆数组,但类型为车辆。根据示例 [Project.Car, Project.MotorCycle]
如果可能的话。
您没有 class 中定义的种类 属性。 另外,Car和MotorCycle没有区别 我在操场上测试了这个,似乎做你想做的事。
class Vehicle: Codable {
enum Kind: Int, Codable {
case car = 0, motorcycle = 1
}
let kind: Kind
let brand: String
let numberOfWheels: Int
}
class Car: Vehicle {}
class MotorCycle: 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)
//But what if I want to decode an array of vehicles, but have them decoded as a specific type?
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]
let allVeh = try! decoder.decode([Vehicle].self, from: combined)
for veh in allVeh where veh.kind == Vehicle.Kind.motorcycle { // } is MotorCycle {
print(veh.brand, type(of: veh))
}
它给出: 雅马哈车辆
你也可以这样写
let allMotorcycles = allVeh.filter {[=11=].kind == Vehicle.Kind.motorcycle}
for moto in allMotorcycles {
print(moto.brand, type(of: moto))
}
这是一个不使用 Codable
的替代解决方案。我还做了一些更改并引入了一个协议而不是超级 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))"
}
}
不是那么重要,但我将类型从 class 更改为 struct
struct Car: Vehicle {
var brand: String
var numberOfWheels: Int
}
struct MotorCycle: Vehicle {
var brand: String
var numberOfWheels: Int
}
然后分两步将 json 转换为 Vehicle
数组,使用 JSONSerialization
进行解码,然后 reduce(into:)
创建对象
do {
if let array = try JSONSerialization.jsonObject(with: combined) as? [[String: Any]] {
let vehicles = array.reduce(into: [Vehicle]()) {
if let kindValue = ["kind"] as? Int,
let kind = VehicleKind(rawValue: kindValue),
let brand = ["brand"] as? String,
let numberOfWheels = ["number_of_wheels"] as? Int {
switch kind {
case .car:
[=12=].append(Car(brand: brand, numberOfWheels: numberOfWheels))
case .motorcycle:
[=12=].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
更新。可编码版本
我还通过引入一种用于解码的单独类型来设法提出一个 Codable 解决方案。设置与之前相同,有一个协议和两个结构。
然后我介绍一个特定的解码类型(但它当然也可以扩展为编码)实现自定义 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)
}
}
}
同样,最终结果是通过 2 个步骤实现的
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([JsonVehicle].self, from: combined)
let vehicles = result.map(\.vehicle)
} catch {
print(error)
}