License Carthage Platforms Build Status codebeat badge

Marshal

在 Swift 中,我们经常需要处理 JSON、plist 以及各种形式的 [String: Any] 数据。Marshal 相信,您无需具备单子或魔镜方面的博士学位,也能以一种富有表现力且类型安全的方式来处理这些数据。Marshal 将利用面向协议编程™ 的强大功能,帮助您编写声明式、高性能、具备错误处理的代码。

用法

使用 Marshal 从 [String: Any] 中提取值非常简单,只需:

let name: String = try json.value(for: "name")
let url: URL = try json.value(for: "user.website") // extract from nested objects!

转换为模型

解组 是指将中间数据格式(已组装 对象)转换为本地表示形式的过程。 可以将组装理解为序列化,将解组理解为反序列化,或者分别理解为编码和解码。

通常,我们希望获取一个已组装的对象(例如 [String: Any])并将其解组为我们的本地模型之一 —— 例如,我们可能希望获取一些 JSON 并使用它来初始化我们的本地模型。

struct User: Unmarshaling {
    var id: Int
    var name: String
    var email: String

    init(object: MarshaledObject) throws {
        id = try object.value(for: "id")
        name = try object.value(for: "name")
        email = try object.value(for: "email")
    }
}

现在,仅需提供一个简单的初始化器,您就可以直接从 [String: Any] 中提取模型

let users: [User] = try json.value(for: "users")

这太简单了!感谢面向协议编程™!

错误处理

您是不顾一切的那种人,不在乎错误吗?使用 try? 给自己一个可选值。 否则,请加入我们这些守法的人,并将您的代码封装在 do-catch 中,以便在出现问题时获取所有有用的详细信息。

添加您自己的值

Marshal 开箱即用地支持提取 Swift 原生类型(如 StringInt 等)、URL、任何符合 Unmarshaling 协议的类型,以及上述所有类型的数组。 它不支持提取更复杂的类型,如 Date,因为日期格式种类繁多等等。

但是,Marshal 不会让你束手无策! 添加你自己的 Marshal 值类型就像用 ValueType 扩展你的类型一样简单。

extension Date : ValueType {
    public static func value(from object: Any) throws -> Date {
        guard let dateString = object as? String else {
            throw MarshalError.typeMismatch(expected: String.self, actual: type(of: object))
        }
        // assuming you have a Date.fromISO8601String implemented...
        guard let date = Date.fromISO8601String(dateString) else {
            throw MarshalError.typeMismatch(expected: "ISO8601 date string", actual: dateString)
        }
        return date
    }
}

只需实现 value(from:),Marshal 就能让您立即做到这一点:

let birthDate: Date = json.value(for: "user.dob")

面向协议编程™ 再次奏效!

回到已组装的对象

我们已经研究了如何从 [String: Any] 转换为本地模型,但反过来又如何呢?

extension User: Marshaling {
    func marshaled() -> [String: Any] {
        return {
            "id": id,
            "name" : name,
            "email": email
        }
    }
}

现在,您可能会想“但是我不能使用反射来自动执行此操作吗?” 你可以。 如果你喜欢那样,有一些其他的优秀框架供你使用。 但是 Marshal 认为镜像可能会导致困境。 Marshal 活在一个所见即所得™ 的世界中,您可以轻松地适应 snake case、camel case 或后端开发人员喜欢的任何 case 的 API。 Marshal 代码是显式且声明式的。 但不要只相信 Marshal 的话 —— 阅读官方 Swift 博客上的 这篇文章的结尾。

性能

当然,如果 Marshal 没有最快的执行速度,它就不会成为 Marshal。 您应该始终以怀疑的态度对待基准测试,但还是先看看 这些基准测试吧。

贡献者

Marshal 最初是 Jason Larsen 关于 JSON 解析的博客系列,但很快演变成一个社区项目。 特别感谢许多人,他们在不同程度上贡献了想法和代码。 其中一些人,按字母顺序排列: