Codable in Swift 4.0

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种常见情况

  1. Flatten out JSON nested property.
  • 展平JSON嵌套属性。
  1. 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通过了我的所有标准,它可以通过易于理解的语法完成我需要的工作。 我没有触及的唯一方面是性能(我认为这会很好)。 我的结论是我肯定将它用于我的下一个项目。

你可能感兴趣的:(Codable in Swift 4.0)