微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

解码基于编码值 Swift 的类

如何解决解码基于编码值 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,\"number_of_wheels\": 4},{\"kind\": 1,\"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] 如果可能的话。

解决方法

这是不使用 import React from "react"; import app from "../images/app.png"; const Featured = () => { return ( <div className="mt-52 "> <h1 className="text-primary-dark-theme text-5xl mt-14">Projects</h1> <div className="grid grid-cols-12 grid-rows-1 mt-20 "> <div className="row-start-1 row-span-1 col-start-1 md:col-span-6"> <img src={app} alt="" className="w-full" /> </div> <div className="row-start-1 row-span-1 col-start-6 col-span-7"> <div className="h-full w-full"> <h1 className="text-right text-primary-dark-theme"> Featured Project </h1> <h1 className="text-right text-gray-300 font-bold ">Title</h1> <p className="p-5 bg-gray-800 text-gray-400 shadow-lg rounded-md mt-5"> Lorem ipsum dolor sit,amet consectetur adipisicing elit. Optio id doloribus sequi repellendus aperiam dolore! </p> </div> </div> </div> </div> ); }; export default Featured; 的替代解决方案。我也做了一些改变并引入了一个协议而不是一个超类。

Codable

不是很重要,但我将类型从 class 更改为 struct

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:)

以上代码输出:

福特,轮子:4,类型:汽车
雅马哈,轮子:2,类型:摩托车


更新。可编码版本

通过引入一个单独的类型用于解码,我也设法提出了一个 Codable 解决方案。设置和之前一样,有一个协议和两个结构体。

然后我介绍了一种用于解码的特定类型(但它当然也可以扩展用于编码),它实现了自定义 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) }

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)
        }
    }
}
,

您没有在类中定义 kind 属性。 另外,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,\"number_of_wheels\": 4},{\"kind\": 1,\"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 {$0.kind == Vehicle.Kind.motorcycle}
for moto in allMotorcycles  {
    print(moto.brand,type(of: moto))
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。