https://medium.com/@sarunw/codable-in-swift-4-0-1a12e38599d8
can it replace JSON encode/decode lib out there?
- 它可以取代JSON编码/解码lib吗?
After I watched WWDC 2017 and heard about Codable I’m thinking of replacing my current JSON encode/decoder in my projects or at least use it in a new one.
- 在我观看WWDC 2017并听说Codable之后,我正在考虑在我的项目中替换当前的JSON编码/解码器,或者至少在新项目中使用它。
I’m happy to see Apple finally come up with this encoder/decoder built into Swift standard lib, since its such a mandatory tasks nowdays and for me I haven’t seen a clear winner in this area. I try to avoid using third part library as much as possible, so I’m really excited to explore its possibility and limitation.
- 我很高兴看到Apple终于推出了内置于Swift标准库中的这种编码器/解码器,因为它现在是如此强制性的任务,而且对于我来说,我还没有看到这个领域的明显赢家。 我尽量避免使用第三方库,所以我很高兴能够探索它的可能性和局限性。
What I looking for in encode/decode?
- 我在编码/解码中寻找什么?
1. Swift optional type supported
- 支持Swift可选类型
The most important thing I need is a Swift optional type supported, this is very cruciel for me, without this it is a deal-breaker.
- 我需要的最重要的是支持Swift可选类型,对我来说这是非常棘手的,如果没有它,这是一个交易破坏者。
Luckily Codable support this. If you have following User Object.
- 幸运的是Codable支持这一点。 如果您有以下用户对象。
struct User: Codable {
var firstName: String
var lastName: String
var middleName: String?
}
These JSON strings are valid.
- 这些JSON字符串有效。
{
"firstName": "John",
"lastName": "Doe",
"middleName": null
}
{
"firstName": "John",
"lastName": "Doe"
}
2. Be able to rename properties
- 能够重命名属性
If you have ever work with REST API you will see that most JSON keys doesn’t use CamelCase naming, but snake_case. Codable
also support rename property keys and its very easy to do so.
- 如果您曾经使用REST API,您将看到大多数JSON密钥不使用CamelCase命名,而是使用snake_case。
Codable
也支持重命名属性键,它很容易实现。
All you need to do is adding a nested enumeration named CodingKeys
that conforms to the CodingKey protocol.
- 您需要做的就是添加一个符合CodingKey协议的名为CodingKeys的嵌套枚举。
From above example we can rename it like this.
- 从上面的例子我们可以像这样重命名它。
struct User: Codable {
var firstName: String
var lastName: String
var middleName: String?
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case middleName = "middle_name"
}
}
And you will be able to decode this JSON
- 并且您将能够解码此JSON
{
"first_name": "John",
"last_name": "Doe",
"middle_name": null
}
{
"first_name": "John",
"last_name": "Doe"
}
3. Custom mapping between JSON and Swift structure.
- JSON和Swift结构之间的自定义映射。
There are cases where we have no control over how JSON look like, be able to have different Swift structure than JSON is nice-to-have feature.
- 在某些情况下,我们无法控制JSON的外观,能够拥有与JSON不同的Swift结构,这是一个很好的功能。
I will test on 2 common cases. 我将测试2种常见情况
- Flatten out JSON nested property.
- 展平JSON嵌套属性。
- Make nested structure from flat JSON.
- 从平面JSON创建嵌套结构。
4. Flatten out JSON
- 展平JSON
Let say you have User JSON that contain nested billingAddress property.
- 假设您有User JSON包含嵌套的billingAddress属性。
{
"name": "John",
"billingAddress": {
"district": "District",
"subDistrict": "Sub District",
"country": "Country",
"postalCode": "Postal Code"
}
}
But somehow you want to layout Swift User like this.
- 但不知何故,你想要像这样布局Swift用户。
struct User: Codable {
var name: String
var district: String
var subDistrict: String
var country: String
var postalCode: String
}
You need to define two enumerations that each list the complete set of coding keys used on a particular level.
- 您需要定义两个枚举,每个枚举列出在特定级别上使用的完整编码密钥集。
struct User: Codable {
....
enum CodingKeys: String, CodingKey {
case name
case billingAddress
}
enum BillingAddressKeys: String, CodingKey {
case district
case subDistrict
case country
case postalCode
}
}
Since this isn’t direct mapping we need to implementing Decodable
's required initializer, init(from:):
- 由于这不是直接映射,我们需要实现Decodable所需的初始化器init(from :):
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
let billingAddress = try values.nestedContainer(keyedBy: BillingAddressKeys.self, forKey: .billingAddress)
district = try billingAddress.decode(String.self, forKey: .district)
subDistrict = try billingAddress.decode(String.self, forKey: .subDistrict)
country = try billingAddress.decode(String.self, forKey: .country)
postalCode = try billingAddress.decode(String.self, forKey: .postalCode)
}
And same apply to Encodable
protocol, a custom encode(to:):
- 同样适用于Encodable协议,自定义编码(至:):
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
var billingAddress = container.nestedContainer(keyedBy: BillingAddressKeys.self, forKey: .billingAddress)
try billingAddress.encode(district, forKey: .district)
try billingAddress.encode(subDistrict, forKey: .subDistrict)
try billingAddress.encode(country, forKey: .country)
try billingAddress.encode(postalCode, forKey: .postalCode)
}
5. Nested structure
- 嵌套结构
This is opposite of what we just did, we got JSON like this.
- 这与我们刚刚做的相反,我们得到了这样的JSON。
{
"name": "John",
"district": "District",
"subDistrict": "Sub District",
"country": "Country",
"postalCode": "Postal Code"
}
And want this Swift structure
- 并希望这个Swift结构
struct User: Codable {
var name: String
var billingAddress: BillingAddress
}
struct BillingAddress: Codable {
var district: String
var subDistrict: String
var country: String
var postalCode: String
}
Following are what we need to implement
- 以下是我们需要实施的内容
struct User: Codable {
....
enum CodingKeys: String, CodingKey {
case name
case billingAddress
case district
case subDistrict
case country
case postalCode
}
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
billingAddress = try BillingAddress(from: decoder)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(billingAddress.district, forKey: .district)
try container.encode(billingAddress.subDistrict, forKey: .subDistrict)
try container.encode(billingAddress.country, forKey: .country)
try container.encode(billingAddress.postalCode, forKey: .postalCode)
}
Conclusion
Codable pass all my criteria, it can do what I needed with easy to understand syntax. The only aspect that I didn’t touch is performance (I think it would be good). My conclusion is I definitely use it for my next project.
- Codable通过了我的所有标准,它可以通过易于理解的语法完成我需要的工作。 我没有触及的唯一方面是性能(我认为这会很好)。 我的结论是我肯定将它用于我的下一个项目。