8

默认情况下,Decodable协议将 JSON 值转换为对象值而不进行任何更改。但有时您需要在 json 解码期间转换值,例如,在您获得的 JSON 中,{id = "id10"}但在您的类实例中,您需要将数字10放入属性中id(甚至放入具有不同名称的属性中)。

您可以实现init(from:)可以使用任何值执行所需操作的方法,例如:

public required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    latitude = try container.decode(Double.self, forKey:.latitude)
    longitude = try container.decode(Double.self, forKey: .longitude)
    // and for example there i can transform string "id10" to number 10
    // and put it into desired field
}

这对我来说听起来很棒,但是如果我只想更改其中一个JSON 字段的值而让我的所有其他 20 个字段保持不变怎么办?如果init(from:)我应该为我班级的 20 个字段中的每一个字段手动获取和放置值!经过多年的 objC 编码,我很直观地首先调用 super 的实现,init(from:)然后只对某些字段进行更改,但是我如何使用 Swift 和Decodable协议实现这样的效果呢?

4

2 回答 2

5

您可以使用lazy var. 缺点是您仍然必须提供键列表,并且不能将模型声明为常量:

struct MyModel: Decodable {
    lazy var id: Int = {
        return Int(_id.replacingOccurrences(of: "id", with: ""))!
    }()
    private var _id: String

    var latitude: CGFloat
    var longitude: CGFloat

    enum CodingKeys: String, CodingKey {
        case latitude, longitude
        case _id = "id"
    }
}

例子:

let json = """
{
    "id": "id10",
    "latitude": 1,
    "longitude": 1
}
""".data(using: .utf8)!

// Can't use a `let` here
var m = try JSONDecoder().decode(MyModel.self, from: json)
print(m.id)
于 2018-02-25T01:59:14.363 回答
2

目前,如果您想更改单个属性的解析,则必须完全实现encodeand方法。decode

Swift Codable 的某些未来版本可能会允许对每个属性的编码和解码进行逐个处理。但是 Swift 的功能工作并非微不足道,还没有被优先考虑:

无论如何,目标可能是提供一个强类型的解决方案,允许您根据具体情况执行此操作,而不会掉下“悬崖”而不得不为了一个属性的全部利益encode(to:而实施init(from:;该解决方案可能很重要,并且需要大量 API 讨论才能弄清楚如何做得好,因此我们还不能做到这一点。

- Itai Ferber,Swift 4 Codable 的首席开发人员

https://bugs.swift.org/browse/SR-5249?focusedCommentId=32638

于 2021-06-24T17:32:04.757 回答