[英]Swift Codable init manually a property
I have struct
that conforms to protocol Codable
.我有符合协议
struct
的Codable
。 Some properties are decoded from JSON fields so I use the CodingKeys
enum for that.一些属性是从 JSON 字段解码的,所以我使用
CodingKeys
枚举。 But there are few properties that are not in my JSON and I need to calculate them from decoded JSON properties.但是我的 JSON 中没有几个属性,我需要从解码的 JSON 属性中计算它们。 For example, if you get a
Zip code
from JSON, I want to calculate City
from it.例如,如果您从 JSON 获得
Zip code
,我想从中计算City
。
I don't want City
to be an optional String.我不希望
City
成为可选字符串。 So I try to calculate it right after my Zip code
field is decoded from JSON.所以我尝试在我的
Zip code
字段从 JSON 解码后立即计算它。
struct Place: Codable {
var name: String
var zipcode: String
// ... Lot of other properties decoded from JSON
var city: String // This property has to be calulated after `zip code` is decoded
enum CodingKeys: String, CodingKey {
case name = "placeName"
case zipcode = "NPA"
// other properties from JSON
}
}
I've triedthis solution to rewrite init(from decoder: Decoder)
.我已经尝试过这个解决方案来重写
init(from decoder: Decoder)
。 But that means I need to manually write each property I need to decode.但这意味着我需要手动编写需要解码的每个属性。 As I have a lot, I would prefer to let default init decoder does it job, and then add my code to calculate
City
.因为我有很多,我宁愿让默认的初始化解码器完成它的工作,然后添加我的代码来计算
City
。
Is there a way to do something like: call default init with decoder, then add some code?有没有办法做类似的事情:用解码器调用默认初始化,然后添加一些代码?
I was also thinking about computed property.我也在考虑计算属性。 But as calculating
City
from Zip code is quite lot of code, I don't want that it is always computed.但是由于从 Zip 代码计算
City
是相当多的代码,我不希望它总是被计算。
I need something like:我需要类似的东西:
init(from decoder: Decoder) throws {
// <- Call default init from decoder
city = CityHelper.city(from: zipcode) // quite heavy code in there
}
I would prefer to let default init decoder does it job, and then add my code to calculate City
我宁愿让默认的初始化解码器完成它的工作,然后添加我的代码来计算城市
Unfortunately you can't.不幸的是你不能。 It is currently all or nothing;
目前是全有或全无; you cannot treat the synthesized init as some sort of inheritance from super (as in your imagined
Call default init
).您不能将合成的 init 视为某种来自 super 的 inheritance (如您想象的
Call default init
)。
I was also thinking about computed property.
我也在考虑计算属性。 But as calculating City from Zip code is quite lot of code, I don't want that it is always computed.
但是由于从 Zip 代码计算 City 是相当多的代码,我不希望它总是被计算。
Use a lazy var property whose initializer calls a method that transforms zip to city.使用惰性 var 属性,其初始化程序调用将 zip 转换为城市的方法。 That way it is calculated, but just once.
这样它就被计算出来了,但只有一次。 The zip will not change, so this is an acceptable compromise.
zip 不会改变,因此这是一个可以接受的折衷方案。
Or even better, use a reducer to transform the decoded struct (with zip) into a completely different struct (with city).或者更好的是,使用 reducer 将解码的结构(带 zip)转换为完全不同的结构(带城市)。
I have found a solution but it needs to use class
instead of struct
.我找到了一个解决方案,但它需要使用
class
而不是struct
。
I store decoded properties in a parent class.我将解码的属性存储在父 class 中。 Then I extend this class and add calculated properties in it.
然后我扩展这个 class 并在其中添加计算属性。
As it is not possible to extend struct
, I had to use class
.由于无法扩展
struct
,我不得不使用class
。 Maybe it is a first step to a better solution and it can help anyone find something better.也许这是迈向更好解决方案的第一步,它可以帮助任何人找到更好的东西。
class DecodedPlace: Codable {
var name: String
var zipcode: String
enum CodingKeys: String, CodingKey {
case name = "placeName"
case zipcode = "NPA"
}
}
class Place: DecodedPlace {
var city: String
required init(from decoder: Decoder) throws {
city = "" // Default value just to call super.init.
try super.init(from: decoder)
city = CityHelper.city(from: zipcode)
}
}
I have to check if using class
instead of struct
is not too bad.我必须检查使用
class
而不是struct
是否还不错。 Any idea?任何想法?
Ok misread at first.好吧,一开始看错了。 Here's something I think could help: use willSet or didSet on zip to then compute the city perhaps?
这是我认为可以帮助的东西:在 zip 上使用 willSet 或 didSet 来计算城市? Not sure if that gets you around not having a default value but this code will only run if the zipcode changes
不确定这是否会让您没有默认值,但此代码仅在邮政编码更改时才会运行
struct Place: Codable {
var name: String
var zipcode: String{
didSet{
//do something after zip is set like calculate city?
}
willSet{
//do something before zip is set.
}
}
// ... Lot of other properties decoded from JSON
var city: String // This property has to be calulated after `zip code` is decoded
enum CodingKeys: String, CodingKey {
case name = "placeName"
case zipcode = "NPA"
// other properties from JSON
}
}
You can use a computed property that relies on the zip to return a city.您可以使用依赖于 zip 的计算属性来返回城市。 So you'd have to figure out the logic of how to map zip back to city with some method but here's a simple example I cooked up:
因此,您必须弄清楚如何通过某种方法将 map zip 返回城市的逻辑,但这是我编写的一个简单示例:
struct Rectangle : Codable{
var width : Float
var height : Float
var area : Float{
return width * height
}
}
Usage:用法:
let myRect = Rectangle(width: 5.0, height: 5.0)
print(myRect.area)
Result: 25.0结果:25.0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.