测试已测试 | ✗ |
语言语言 | SwiftSwift |
许可 | MIT |
发布上次发布 | 2017年5月 |
SwiftSwift版本 | 3.0 |
SPM支持 SPM | ✗ |
依赖于 | |
SwiftyJSON | ~> 3.0 |
Curry | ~> 3.0 |
一个用于使用管道操作符((|>)
)和普通函数调用构建JSON解码器的库。受到elm-decode-pipeline的启发。
作为一名Swift的新手,我并不完全相信现有的JSON解码库。在尝试了Elm之后,我希望可以得到一些简单且主要基于函数的库。
Argo引起了我的注意。虽然它在很大程度上符合我的需求,但它强制你在你的类型中实现一个decode
方法。这样做允许Argo自动解码任何实现了Decodable
协议的类型。但是,这种方法不够灵活。它将一个(且仅一个)解码器绑定到特定的类型,永久不可更改。如果我们想将不同API的响应解码为同一个类型怎么办?如果某些端点不返回完全相同的数据怎么办?这是否听起来很熟悉?另外,Argo使用了许多不同的中缀操作符(如<^>
,<*>
,<|
,<||
),这使得解码器定义难以理解和推理。
我认为解码器应该是独立的纯净函数,它们从类型中独立存在。它们接收一些数据(目前为JSON)并产生一个错误或某个类型的实例。解码器应该容易组合和重用,而这只需要一个简单的中缀操作符:管道操作符(|>)
。这种方法在elm-decode-pipeline中得到了很好的实现,这是一个Elm语言库,它产生了简单、可读和可重用的解码器。
SwiftDecodePipeline试图将elm-decode-pipeline的精神带入Swift。
## 示例
假设我们有一个这个类型
struct User {
let name: String
let surname: String
let image: String?
let score: Int
let sports: [String]
let role: String
}
我们需要解码一些API端点/users
返回的数据,如下所示
[
{
"uuid": "...",
"name": "John",
"last_name": "Doe",
"image": null,
"sports": ["basketball", "tennis"],
"score": 5
}
]
然后我们可以为该端点轻松编写一个Decoder<User>
let decodeUser: Decoder<User> =
decode(User.init)
|> required("name", string)
|> required("last_name", string)
|> optional("image", string)
|> required("score", int)
|> required("sports", array(string))
|> hardcoded("athlete")
现在我们可以解码响应
let data: String!
// We make a request to /users and obtain the JSON here...
let result = decodeJSON(data, with: array(decodeUser))
switch result {
case .error(let error): print("Invalid format: \(error)") // error describes the decoding error
case .ok(let users): doSomething(with: users) // users has type [User] :D
}
在上面的示例中,data
是一个 String
。然而,decode
函数支持不同的类型作为第一个参数以适应您的需求。下一节将描述可以使用此库解码的不同数据类型。
注意将解码器转换和重复使用的简单性。在这种情况下,我们的 decodeUser
可以解码单个用户,但我们想解码一串用户。因此,我们最终使用 array(decodeUser)
将我们的 Decoder
转换为 Decoder<[User]>
。一个 Decoder
在格式无效时返回 .error(String)
,或者在输入成功解码时返回 .ok(Type)
。
## 可用函数 ### decodeJSON(json, with: decoder)
它允许以便捷的方式在 JSON 解码中使用解码器。目前,有 3 个不同的 decodeJSON
定义。它们接受 JSON 的不同形式:Data
、String
和 Any
。
Any
定义是为了 支持 Alamofire 响应数据。
原始数据类型本身就是解码器。它们将 JSON 值解码为 Swift 值。
let string: Decoder<String>
let bool: Decoder<Bool>
let int: Decoder<Int>
let double: Decoder<Double>
### 修饰符
修饰符是一个可以接收一些配置参数并返回一个函数的函数,该函数可以接收一个解码器并返回一个新的解码器,该解码器具有一些附加行为。
在本节中,使用 <modifier>(<configParams>)
语法来描述不同修饰符。
required(String, Decoder)
和 optional(String, Decoder)
required
从 JSON 对象中提取一个字段并解码其值为 A
,如果字段不存在或为 null
则失败。optional
会执行相同的操作,同时允许字段缺失或为 null
,因此解码为 A?
。
struct User {
let name: String
let image: String?
}
let decodeUser: Decoder<User> =
decode(User.init)
|> required("name", string)
|> optional("image", string)
array
它解码 JSON 数组。
struct Post {
// ...
let authors: [User]
// ...
}
let decodePost: Decoder<Post> =
decode(Post.init)
//...
|> required("authors", decodeUser |> array)
// We could use array(decodeUser), they are equivalent
// ...
hardcoded(A)
它总是返回提供的值,独立于 JSON 数据。它用于在解码管道中硬编码数据。
let decodeMockedUser: Decoder<User> =
decode(User.init)
|> hardcoded("some-id")
|> hardcoded("John")
|> hardcoded("Doe")
// ...
map((A) -> B)
它将解码值从 A
转换为 B
。
let decodeLowercasedString: Decoder<String> = string |> map { $0.lowercased }
## 库支持
此库可以轻松与 Alamofire 一同使用
Alamofire.request("https://example.com/users").validate().responseJSON { response in
switch response.result {
case .success(let data):
// We want to decode a list of users, so we use array
let decodingResult = decodeJSON(data, with: array(decodeUser))
switch decodingResult {
case .error(let error): print("Invalid format: \(error)") // error describes the decoding error
case .ok(let users): doSomething(with: users) // users has type [User]
}
// Handle request error here...
}
}
### SwiftyJSON
此库内部使用 SwiftyJSON。您应该能够直接使用解码器解码 JSON
类型。
let json: JSON!
let result = decodeUser(json)
SwiftDecodePipeline 可通过 CocoaPods 使用。要安装它,请将以下行添加到您的 Podfile 中
pod 'SwiftDecodePipeline'
Héctor Ramón Jiménez
SwiftDecodePipeline 在 MIT 许可证下提供。有关更多信息,请参阅 LICENSE 文件。
## 贡献