如何在Swift中将属性动态添加到恒定的Decodable对象?

时间:2019-01-04 13:33:45

标签: json swift decodable

背景

基本上,我有一个返回如下内容的api:

    "order_detail": [
        {
            "id": 6938,
            "order_id": 6404,
            "item_price": "4",
            ..
            "item": {
                "id": 12644,
                "ref": "Iced Caffe Americano",
                "short_description": "",
                ..

在可解码的obj中,我得到了

public struct OrderDetail: Decodable {
    public let id: Int
    public let order_id: Int
    public let item_price: String?
    ..
    public let item: Item?

public struct Item: Decodable {
    public var id: Int
    public var ref: String?
    public var short_description: String?

问题在于代码中的其他地方,有一种方法期望Item对象具有item_price

问题

我想要做的是混淆或更改此常量Item对象,并向其动态添加item_price属性。我该怎么做?

解决方法,其他解决方案

1。更改json

我知道针对同一问题还有很多其他解决方案(正如我们所说的,我正在研究它,只是简单地修改api端点以满足我的需要)。但是同样,该选项并非总是可行的(即,假设后端团队是独立的)

2。更改功能期望

这也是可能的,但也不便宜,因为此功能在应用程序中许多其他我无法控制的地方都使用了

2 个答案:

答案 0 :(得分:2)

如果要向不属于其JSON表示形式的Decodable类型添加属性,则只需声明一个CodingKey一致类型并省略特定的属性名称,以便自动合成的init(from decoder:Decoder)初始化程序将知道不要在JSON中查找该值。

此外,您还应该遵守Swift命名约定(lowerCamelCase表示变量名),并使用CodingKey将JSON键映射到属性名。

public struct Item: Decodable {
    public var id: Int
    public var ref: String?
    public var shortDescription: String?
    public var itemPrice: String? // or whatever else its type needs to be

    private enum CodingKeys: String, CodingKey {
        case id, ref, shortDescription = "short_description"
    }
}

答案 1 :(得分:0)

这是实现这一目标的一种方法

接管Item在OrderDetail解码中的初始化。

struct OrderDetail: Decodable {
    let id: Int
    let orderId: Int
    let itemPrice: String?
    let item: Item

    private enum OrderDetailCodingKey: CodingKey {
        case id
        case orderId
        case itemPrice
        case item
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: OrderDetailCodingKey.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.orderId = try container.decode(Int.self, forKey: .orderId)

        let itemPrice = try container.decode(String?.self, forKey: .itemPrice)
        self.itemPrice = itemPrice
        self.item = try Item(from: decoder, itemPrice: itemPrice)
    }
}

使用自定义初始化程序创建您的商品。

struct Item: Decodable {
    let id: Int
    let ref: String?
    let shortDescription: String?
    let itemPrice: String?

    private enum ItemCodingKeys: CodingKey {
        case id
        case ref
        case shortDescription
    }

    init(from decoder: Decoder, itemPrice: String?) throws {
        let container = try decoder.container(keyedBy: ItemCodingKeys.self)
        self.id = try container.decode(Int.self, forKey: .id)
        self.ref = try? container.decode(String.self, forKey: .ref)
        self.shortDescription = try? container.decode(String.self, forKey: .shortDescription)
        self.itemPrice = itemPrice
    }
}

您可以调用以下函数来测试该功能:

private func test() {
        let json = """
            {"id":6938,"orderId":6404,"itemPrice":"4","item":{"id":12644,"ref":"Iced Caffe Americano","shortDescription":""}}
        """
        let data = json.data(using: .utf8)
        let decoder = JSONDecoder()
        if let data = data {
            do {
                let order = try decoder.decode(OrderDetail.self, from: data)
                print(order)
            } catch let jsonError {
                os_log("JSON decoding failed [%@]", String(describing: jsonError))
            }
        } else {
            os_log("No data found")
        }
    }