Decodable 版本 0.6.0

Decodable 0.6.0

测试测试
语言语言 SwiftSwift
许可证 MIT
发布最新发布2017 年 9 月
SwiftSwift 版本3.0
SPM支持 SPM

David OhayonJohannes Lund 维护。



Decodable 0.6.0

  • Anviking

Decodable

简单、严格,并且强大的对象映射(通过 Swift 2 的错误处理实现)。深受 Argo 启发,但没有亿万级功能操作符。

struct Repository {
    let name: String
    let description: String
    let stargazersCount: Int
    let language: String?
    let sometimesMissingKey: String?

    let owner: User // Struct conforming to Decodable
    let defaultBranch: Branch // Struct NOT conforming to Decodable

    var fullName: String { return "\(owner.login)/\(name)" }
}

extension Repository: Decodable {
    static func decode(j: Any) throws -> Repository {
        return try Repository(
                    name: j => "nested" => "name", 
                    description: j => "description", 
                    stargazersCount: j => "stargazers_count", 
                    language: j => "language", 
                    sometimesMissingKey: j =>? "sometimesMissingKey",
                    owner: j => "owner", 
                    defaultBranch: Branch(name: j => "default_branch")
                )
    }
}

do {
    let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
    let repo = try [Repository].decode(json)
} catch {
    print(error)
}

它是如何工作的?

一个协议

public protocol Decodable {
    static func decode(json: AnyObject) throws -> Self
}

一个解析函数

public func parse<T>(json: AnyObject, path: [String], decode: (AnyObject throws -> T)) throws -> T

和无耻的操作符重载

太多的生成的重载,都调用了 parse 函数,可以在 Overloads.swift 中找到。返回类型包括 T?[T?][T?]?AnyObject[String: T]?。当 Swift 支持条件协议遵循时,这一点将不再是必要的,并且自动解码无限嵌套泛型类型(如 [[[[[[[[[A???]]: B]]]?]]?]])将工作。

一个重载可能看起来像这样

public func => <T: Decodable>(json: AnyObject, keyPath: KeyPath) throws -> T

键路径

键路径可以由字符串字面量和数组字面量以及显式初始化器创建。它们也可以使用 =>=>? 操作符连接。=>? 是另一个操作符,表示如果右侧缺乏键则应返回 nil

  • 当在同一个键路径中使用 =>=>? 操作符时,=> 的严格性仍然得到尊重。
  • 可选键路径(=>?)需要一个可选返回类型
let a: KeyPath = "a"
let b: KeyPath = ["a", "b"]
let c: KeyPath = "a" => "b" => "c"
let string: String? = json =>? "key1" => "key2" => "key3"`
                                ^^^^ allowed to be missing

错误

错误将在解码过程中被捕捉并重新抛出,以回传元数据,例如失败解码的 JSON 对象,它的键路径和根 JSON 对象。

有关更多的信息请查看 DecodingError.swift

public enum DecodingError: ErrorProtocol, Equatable {
    /// Thrown when optional casting from `AnyObject` fails.
    ///
    /// This can happen both when trying to access a key on a object
    /// that isn't a `NSDictionary`, and failing to cast a `Castable`
    /// primitive.
    case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)

    /// Thrown when a given, required, key was not found in a dictionary.
    case missingKey(String, Metadata)

    /// Thrown from the `RawRepresentable` extension when
    /// `init(rawValue:)` returned `nil`.
    case rawRepresentableInitializationError(rawValue: Any, Metadata)

    /// When an error is thrown that isn't `DecodingError`, it 
    /// will be wrapped in `DecodingError.other` in order to also provide
    /// metadata about where the error was thrown.
    case other(ErrorProtocol, Metadata)
}
let dict: NSDictionary = ["object": ["repo": ["owner": ["id" : 1, "login": "anviking"]]]]

do {
    let username: String = try dict => "object" => "repo" => "owner" => "name"
} catch let error {
    print(error)
}
//
// MissingKeyError at object.repo.owner: name in {
//    id = 1;
//    login = anviking;
// }

处理错误

j => "key" 之类的表达式将直接抛出错误,并可以使用 catch 语句创建最复杂的错误处理行为。这也意味着可以使用 try? 返回 nil,而不是抛出错误。

为了方便,有一个操作符 =>?,它只在对缺失的键返回 nil。这种 API 用这种方式指示 null,以帮助处理不同的响应格式。

重载 空值行为 缺失键的行为 类型不匹配的行为 子对象中的错误
=> -> T 抛出异常 抛出异常 抛出异常 未捕获(抛出异常)
=> -> T? nil 抛出异常 抛出异常 未捕获(抛出异常)
=>? -> T? nil nil 抛出异常 未捕获(抛出异常)
try? => -> T nil nil nil 捕获(nil)

自定义

类型如 IntDoubleStringBoolDate(ISO8601)、NSArrayNSDictionary,它们必须遵守以下声明以符合 DynamicDecodable

public protocol DynamicDecodable {
    associatedtype DecodedType
    static var decoder: (Any) throws -> DecodedType {get set}
}

这样允许 Decodable 在需要时覆盖默认的解码闭包。

// Lets extend Bool.decoder so that it accepts certain strings:
Bool.decoder = { json in
    switch json {
    case let str as String where str == "true":
        return true
    case let str as String where str == "false":
        return false
    default:
        return try cast(json)
    }
}

注意,当扩展新类型以符合 Decodable 时,实际上没有理由遵守 DynamicDecodable,因为您已经控制了实现。另外,请注意 decoder 属性是作为“一次性设置”的。如果您需要在不同的场合有不同的行为,请创建自定义的解码函数。

默认的 Date.decoder 使用 ISO8601 日期格式化程序。如果您不想创建自己的解码闭包,有一个辅助程序

Date.decoder = Date.decoder(using: formatter)

Decodable 不够的时候

不要害怕不遵守 Decodable

let array = try NSArray.decode(json => "list").map {
    try Contribution(json: $0, repository: repo)
}

提示

  • 您可以在类中使用 Decodable。只需确保对 self 调用一个 required 初始化器(例如 self.init)并返回 Self,或者将您的类设置为 final。( 这可能是一个问题
  • 不应该因为 Decodable-协议和 =>-运算符而让您不得不在所有地方使用它们。

兼容性

Swift 版本 兼容标签或分支
Swift 3.0 v0.5
Swift 2.3 v0.4.4
Swift 2.2 v0.4.3