json - 基于编码值Swift解码类
问题描述
我正在尝试根据编码数据的内容解码特定的类。
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 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
}
Vehicle
然后分两步将json转换为数组,JSONSerialization
用于解码,然后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)
}
上面的代码输出:
福特, 轮子: 4, 类型: Car
Yamaha, 轮子: 2, 类型: 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)
}
推荐阅读
- java - 从对角二维数组中获取元素
- postgresql - postgres 中的 BIGSERIAL 线程安全吗?
- python - 如何在 Python 处理中将变量设置为 = 宽度或高度?
- javascript - 尝试删除多于一行时的奇怪行为
- r - R删除特定字符后跟街道地址中的数字
- python - 如何根据重复的 id 填充 NaN?
- python - 尝试从 python tornado 服务器将内容呈现为 html 时出错
- image - 如何提取图像元数据 Dart/Flutter
- ssl-certificate - java api从同一个pem文件中读取证书和CRL
- maven - 为什么即使我已经在 pom.xml 中包含了依赖项,我也会收到 NoClassDefFoundError?