简体   繁体   English

Swift 可解码:如何在解码过程中转换其中一个值?

[英]Swift Decodable: how to transform one of values during decoding?

Be default, Decodable protocol makes translation of JSON values to object values with no change.默认情况下, Decodable协议将 JSON 值转换为 object 值,没有任何变化。 But sometimes you need to transform value during json decoding, for example, in JSON you get {id = "id10"} but in your class instance you need to put number 10 into property id (or into even property with different name).但有时您需要在 json 解码期间转换值,例如,在 JSON 中您得到{id = "id10"}但在您的 class 实例中您需要将数字10放入属性id (或什至具有不同名称的属性)。

You can implement method init(from:) where you can do what you want with any of the values, for example:您可以实现方法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
}

Thats sounds great for me, but what if i want to change value just for one of the JSON fields and left all my other 20 fields with no change?这对我来说听起来不错,但如果我只想更改 JSON 字段之一的值,而让其他 20 个字段保持不变怎么办? In case of init(from:) i should manually get and put values for every of 20 fields of my class!init(from:)的情况下,我应该为我班级的 20 个字段中的每一个字段手动获取和放置值! After years of objC coding it's intuitive for me to first call super's implementation of init(from:) and then make changes just to some fields, but how i can achieve such effect with Swift and Decodable protocol?经过多年的 objC 编码,我很直观地首先调用 super 的init(from:)实现,然后仅对某些字段进行更改,但我如何使用 Swift 和Decodable协议实现这样的效果?

You can use a lazy var . 你可以使用lazy var The downside being that you still have to provide a list of keys and you can't declare your model a constant: 缺点是你仍然需要提供一个键列表,你不能声明你的模型是一个常量:

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"
    }
}

Example: 例:

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)

Currently you are forced to fully implement the encode and decode methods if you want to change the parsing of even a single property.当前,如果您想要更改甚至单个属性的解析,则必须完全实现encodedecode方法。

Some future version of Swift Codable will likely allow case-by-case handling of each property's encoding and decoding. Swift Codable 的某些未来版本可能会允许逐个处理每个属性的编码和解码。 But that Swift feature work is non-trivial and hasn't been prioritized yet:但是 Swift 功能工作非常重要,尚未确定优先级:

Regardless, the goal is to likely offer a strongly-typed solution that allows you to do this on a case-by-case basis with out falling off the "cliff" into having to implement all of encode(to: and init(from: for the benefit of one property; the solution is likely nontrivial and would require a lot of API discussion to figure out how to do well, hence why we haven't been able to do this yet.无论如何,我们的目标是可能提供一种强类型的解决方案,允许您根据具体情况执行此操作,而不会掉下“悬崖”而不得不实施所有encode(to:init(from:为了一个财产的利益;解决方案可能很重要,需要大量 API 讨论才能弄清楚如何做好,因此我们还不能做到这一点。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM