冗余编码方案常用两种校验码_3种解决方案可帮助您快速编码

冗余编码方案常用两种校验码

Swift 4 introduced the Codable protocol to decode/encode JSON objects. Codable is tightly integrated into the Swift toolchain. It is widely used on client-side and server-side Swift.

Swift 4引入了Codable协议来解码/编码JSON对象。 Codable已紧密集成到Swift工具链中。 它广泛用于客户端和服务器端Swift。

Sometimes, we meet challenges that require us to customize how we use Codable to suit different advanced scenarios. Here are three hard challenges I’ve met when using Codable and my clean code solutions.

有时,我们遇到的挑战要求我们自定义如何使用Codable来适应不同的高级方案。 在使用Codable和干净代码解决方案时,这是我遇到的三个难题。

相同键的多种值类型 (Multiple Value Types for the Same Key)

假设的API (A hypothetical API)

Consider a hypothetical API that sometimes returns a String and sometimes returns an Int for the key id:

考虑一个假设的API,该API有时返回一个String ,有时返回一个Int作为键id

[
    {
      "id": "XA134RRW",
      "name": "Bob"
    },
    {
      "id": 3003,
      "name": "John"
    }
]

The most common and straightforward solution is using an Enum to map to the different cases. Do we have the clean code solution to avoid the boilerplate rawValue it introduces?

最常见,最直接的解决方案是使用枚举映射到不同的情况。 我们是否有干净的代码解决方案来避免它引入的样板rawValue

干净的代码解决方案 (The clean code solution)

To reduce the boilerplate and make clean code, I’m using the PropertyWrapper:

为了减少样板并编写清晰的代码,我使用了PropertyWrapper

// MARk: LosslessStringCodable
public typealias LosslessStringCodable = LosslessStringConvertible & Codable


// MARK: DynamicDecoder
public struct DynamicCoder<T: LosslessStringCodable> {
    public static func decode(from decoder: Decoder) throws -> T? {
        do {
            return try T(from: decoder)
        } catch {
            // Handle different types for the same key error
            func decode<T: LosslessStringCodable>(_: T.Type) -> (Decoder) -> LosslessStringCodable? {
                return { try? T.init(from: $0) }
            }
            let types: [(Decoder) -> LosslessStringCodable?] = [
                decode(String.self),
                decode(Bool.self),
                decode(Int.self),
                decode(Double.self)
            ]


            guard let rawValue = types.lazy.compactMap({ $0(decoder) }).first,
                let value = T.init("\(rawValue)") else {
                    return nil
            }


            return value
        }
    }
}


// MARK: CodableValue
@propertyWrapper
public struct CodableValue<T: LosslessStringCodable>: Codable {
    public let wrappedValue: T


    public init(wrappedValue: T) {
        self.wrappedValue = wrappedValue
    }


    public init(from decoder: Decoder) throws {
        let value: T = try DynamicCoder.decode(from: decoder)!
        self.init(wrappedValue: value)
    }


    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(wrappedValue)
    }
}

Let’s break down the code bit by bit:

让我们逐一分解代码:

  • The LosslessStringCodable protocol is a combination of the LosslessStringConvertible and Codable protocols. For any type that can be String convertible, it can use the generic type T.

    LosslessStringCodable协议是LosslessStringConvertibleCodable协议的组合。 对于任何可以String转换的类型,都可以使用通用类型T

  • DynamicCoder accepts the generic type LosslessStringCodable. It can decode different types, including String, Bool, Int, and Double, to the LosslessStringCodable type. It can be easily expanded to other types.

    DynamicCoder接受通用类型LosslessStringCodable 。 它可以将不同类型(包括StringBoolIntDouble解码为LosslessStringCodable类型。 它可以轻松扩展为其他类型。

  • The CodableValue is a property wrapper that decodes and encodes the wrappedValue to the desired generic type.

    所述CodableValue是包装器,解码的属性和编码wrappedValue到所需的通用类型。

使用范例 (Example use)

// MARK: User
struct User: Codable {
    @CodableValue
    var id: String //This is how we use the property wrapper
    let name: String
}

The struct User uses the CodableValue property wrapper with the ID as String value, but it surely can be used as other types.

struct User使用ID为String值的CodableValue属性包装器,但是可以肯定将其用作其他类型。

动态键 (Dynamic Keys)

假设的API (A hypothetical API)

Consider a hypothetical API that returns JSON objects in order as the key:

考虑一个假设的API,该API按顺序返回JSON对象作为键:

{
  "queues": [
    {
      "1": {
        "firstName": "Bob",
        "lastName": "Dylan"
      }
    },
    {
      "2": {
        "firstName": "Tom",
        "lastName": "Hanks"
      }
    }
  ]
}

干净的代码解决方案 (The clean code solution)

Using the dictionary to decode the JSON will be a suitable solution:

使用字典解码JSON将是合适的解决方案:

// MARK: Result
struct Result: Codable {
    let queues: [[String: Customer]]
}


// MARK: Customer
struct Customer: Codable {
    let firstName: String
    let lastName: String
}

使用范例 (Example use)

Convert the JSON to data and use the JSONDecoder to decode it:

将JSON转换为数据,然后使用JSONDecoder进行解码:

let jsonWithOrderData = jsonWithOrder.data(using: .utf8)!
let result = try! JSONDecoder().decode(Result.self, from: jsonWithOderData)
print(result) 
// Result(queues: [["1": Customer(firstName: "Bob", lastName: "Dylan")], 
//                 ["2": Customer(firstName: "Tom", lastName: "Hanks")]])

具有协议属性的可编码结构 (Codable Struct With Protocol Property)

假设的API (A hypothetical API)

Consider a hypothetical API that returns the published pieces with the different types Article and Brief:

考虑一个假设的API,该API返回具有不同类型的ArticleBrief的已发布Article

{
  "state": "published",
  "contents": [
    {
      "type": "Article",
      "id": "1",
      "title": "An article title"
    },
    {
      "type": "Brief",
      "id": "2",
      "title": "A brief title"
    }
  ]
}

干净的代码解决方案 (The clean code solution)

enum PublicationType: String, Codable {
    case article
    case brief


    public var type: Publication.Type {
        switch self {
        case .article:
            return Article.self
        case .brief:
            return Brief.self
        }
    }
}


// MARK: ContentMapper
struct PublicationMapper: Codable {
    let publication: Publication


    init(_ publication: Publication) {
        self.publication = publication
    }


    enum CodingKeys: String, CodingKey {
        case metatype = "type"
        case content
    }


    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        let typeString = String(describing: type(of: publication)).lowercased()
        guard let metatype = PublicationType(rawValue: typeString) else {
            throw NSError(domain: "Encoding", code: -1)
        }
        try container.encode(metatype, forKey: .metatype)
        try publication.encode(to: encoder)
    }


    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        guard let typeString = try? container.decode(String.self, forKey: .metatype).lowercased(),
            let publicationType = PublicationType(rawValue: typeString) else {
            throw NSError(domain: "Encoding", code: -1)
        }
        self.publication = try publicationType.type.init(from: decoder)
    }
}

Let’s break down the code:

让我们分解一下代码:

  • PublicationType contains two cases: Article and Brief. The most important part is it returns different struct types based on the cases.

    PublicationType包含两种情况: ArticleBrief 。 最重要的部分是它根据情况返回不同的结构类型。

  • PublicationMapper has a value publication and also a hidden value metatype that maps the type of the protocol.

    PublicationMapper具有值publication和映射协议类型的隐藏值metatype类型。

  • PublicationMapper implements the encoder function to encode the publication to the specific struct type.

    PublicationMapper实现编码器功能,以将publication编码为特定的结构类型。

  • PublicationMapper also implements the decoder function to decode JSON to the particular struct type by detecting the meta-type.

    PublicationMapper还实现了解码器功能,通过检测元类型将JSON解码为特定的结构类型。

使用范例 (Example use)

struct Publican: Codable {
    let state: String
    let contents: [Publication]


    enum CodingKeys: String, CodingKey {
        case state
        case contents
    }


    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(state, forKey: .state)
        try container.encode(contents.map({ PublicationMapper($0) }), forKey: .contents)
    }


    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        state = try container.decode(String.self, forKey: .state)
        contents = try container.decode([PublicationMapper].self, forKey: .contents).map({ $0.publication })
    }
}

The publican struct is implemented with the customized CodingKeys and encoder/decoder functions to map the particular struct into the contents array.

publican结构与实施定制CodingKeys和编码器/解码器功能来映射特定结构到contents阵列。

let publicationData = publicationJSON.data(using: .utf8)!
let publican = JSONDecoder().decode(Publican.self, from: publicationData)
print(publican)
//Publican(state: "published", contents: [Article(id: "1", title: "An article title"), 
//                                        Brief(id: "2", title: "A brief title")])

结论 (Conclusion)

Using Codable makes working with JSON serialization much easier. If you’re interested, I published another piece about how Codable compares to SwiftyJSON.

使用Codable使JSON序列化工作变得更加容易。 如果您有兴趣,我还会发表另一篇文章,介绍Codable与SwiftyJSON的比较。

All the code mentioned in this article can be found on GitHub.

可以在GitHub上找到本文提到的所有代码。

翻译自: https://medium.com/better-programming/3-solutions-help-you-swift-codable-cfd8de188676

冗余编码方案常用两种校验码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值