将JSON数组中的项反序列化为具有关联值的不同枚举值

7vhp5slm  于 2023-03-04  发布在  其他
关注(0)|答案(1)|浏览(112)

我正在尝试反序列化一个json,如下所示

{
    "timestamp": 123456789,
    "ownerAssets" : [
        {
            "ownerId" : 123,
            "location" : "USA",
            "assets" : [
                {
                    "car" : true,
                    "make" : "honda",
                    "model" : "crv"
                },
                {
                    "fruit" : true,
                    "name" : "apple",
                    "sweetness" : "high"
                    "count": 5
                }
            ]
        },
        {
            "ownerId" : 456,
            "location" : "USA",
            "assets" : [
                {
                    "car" : true,
                    "make" : "toyota",
                    "model" : "highlander"
                },
                {
                    "fruit" : true,
                    "name" : "orange",
                    "sweetness" : "low",
                    "count": 5
                }
            ]
        }
    ]
}

我如何设计DTO模型来解析它呢?我希望避免使用协议,而更喜欢使用具有关联值的枚举。
我认为以下几点是显而易见的:

struct StuffDTO: Codable {
    let timestamp: Int
    let ownerAssets: [OwnerAsset]
}

extension StuffDTO {
    struct OwnerAsset: Codable {
        let ownerId: Int
        let location: String
        let assets: [Asset]
    }
}

我可以定义一个uber对象,它可以将所有内容和if-then-else解析为不同的枚举值(见下文),这些枚举值具有不同类型的关联值

struct Asset: Codable {
    // car fields
    let car: Bool?
    let make: String?
    let model: String?
    
    // fruit fields
    let fruit: Bool?
    let name: String?
    let sweetness: String?
    let count: Int?
}

但是我想知道是否有一种聪明的方法可以直接将json解析成枚举值,而不需要解析成一个uber对象或者不使用多态性/协议。

enum Asset: Codable {
    case car(CarDTO)
    case fruit(FruitDTO)

    init() {
        // what would this custom json parsing logic look like?
    }
}

struct CarDTO: Codable {
    let make: String
    let model: String
}

struct FruitDTO: Codable {
    let name: String
    let sweetness: String
    let count: Int
}
lf5gs5x2

lf5gs5x21#

首先应定义一个中间模型,如下所示:

struct BaseAsset: Codable {
    let type: AssetType
    let fruit: Bool
    let car: Bool

    enum AssetType {
        case car
        case fruit
    }

    enum CodingKeys: String, CodingKey {
        case fruit
        case car
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        car = try container.decodeIfPresent(Bool.self, forKey: .car) ?? false
        fruit = try container.decodeIfPresent(Bool.self, forKey: .fruit) ?? false
        type = car ? .car : .fruit
    }
}

然后像这样编写资产解码器:

enum Asset: Codable {
    case car(CarDTO)
    case fruit(FruitDTO)
    case unknown

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let type = try container.decode(BaseAsset.self).type
        switch type {
        case .car:
            self = .car(try container.decode(CarDTO.self))
        case .fruit:
            self = .fruit(try container.decode(FruitDTO.self))
        }
    }
}

相关问题