使用 swift_2.3
分支来获取兼容的 Gloss 版本和示例项目。
目前通过 CocoaPods 和 Carthage 可获得的 Gloss 源代码与 Swift 3.0 兼容。
import PackageDescription
let package = Package(
name: "HelloWorld",
dependencies: [
.Package(url: "https://github.com/hkellaway/Gloss.git", majorVersion: 1, minorVersion: 2)
]
)
让我们想象我们有一个如下 JSON 表示的简单模型
{
"id" : 5456481,
"login" : "hkellaway"
}
我们的 Gloss 模型将看起来像这样
import Gloss
struct RepoOwner: Decodable {
let ownerId: Int?
let username: String?
// MARK: - Deserialization
init?(json: JSON) {
self.ownerId = "id" <~~ json
self.username = "login" <~~ json
}
}
此模型
Gloss
Decodable
协议init?(json:)
构造函数(注意:如果不想使用自定义操作符如 <~~
,请参阅 关于不使用 Gloss 操作符。)
此前的示例显示的模型只有可选属性,即所有属性都以 ?
结尾。如果您确定用于创建您模型的 JSON 将始终具有您属性的值,则可以将这些属性表示为非可选的。
非可选属性需要在 init?(json:)
方法中使用额外的 guard
语句来确保在运行时值可用。如果值不可用,则应返回 nil
。
让我们假设我们知道 RepoOwner
属性的 ownerId
将始终可用
import Gloss
struct RepoOwner: Decodable {
let ownerId: Int
let username: String?
// MARK: - Deserialization
init?(json: JSON) {
guard let ownerId: Int = "id" <~~ json else {
return nil
}
self.ownerId = ownerId
self.username = "login" <~~ json
}
}
此模型在两个方面发生了变化
ownerId
属性不再是可选的init?(json:)
初始化器现在有一个 guard
语句,用于仅检查非可选属性。让我们想象一下我们有一个更复杂的模型,该模型如下所示
{
"id" : 40102424,
"name": "Gloss",
"description" : "A shiny JSON parsing library in Swift",
"html_url" : "https://github.com/hkellaway/Gloss",
"owner" : {
"id" : 5456481,
"login" : "hkellaway"
},
"language" : "Swift"
}
这个模型更复杂的原因有几个
owner
我们的 Gloss 模型将看起来像这样
import Gloss
struct Repo: Decodable {
let repoId: Int?
let name: String?
let desc: String?
let url: NSURL?
let owner: RepoOwner?
let primaryLanguage: Language?
enum Language: String {
case Swift = "Swift"
case ObjectiveC = "Objective-C"
}
// MARK: - Deserialization
init?(json: JSON) {
self.repoId = "id" <~~ json
self.name = "name" <~~ json
self.desc = "description" <~~ json
self.url = "html_url" <~~ json
self.owner = "owner" <~~ json
self.primaryLanguage = "language" <~~ json
}
}
尽管更复杂,但此模型也非常简单易构建 - 常见类型,例如 NSURL
、一个 enum
值和其他 Gloss 模型 RepoOwner
,都可以在不增加额外开销的情况下处理!
-(注意:如果嵌套模型在 JSON 中存在,但在您的 Gloss 模型中不希望存在,请参阅不创建额外模型检索嵌套模型值.)
接下来,我们如何允许模型转换为 JSON?让我们再次看一下 RepoOwner
模型
import Gloss
struct RepoOwner: Glossy {
let ownerId: Int?
let username: String?
// MARK: - Deserialization
// ...
// MARK: - Serialization
func toJSON() -> JSON? {
return jsonify([
"id" ~~> self.ownerId,
"login" ~~> self.username
])
}
}
该模型现在
Glossy
协议toJSON()
方法,该方法调用 jsonify(_:)
函数(注意:如果不想使用像 ~~>
这样的自定义操作符,请参阅不使用 Gloss 操作符.)
通过调用 init?(json:)
创建 Decodable
Gloss 模型的实例。
例如,我们可以创建一个 RepoOwner
如下所示
let repoOwnerJSON = [
"id" : 5456481,
"name": "hkellaway"
]
guard let repoOwner = RepoOwner(json: repoOwnerJSON) else {
// handle decoding failure here
}
print(repoOwner.repoId)
print(repoOwner.name)
或者,使用 if let
语法
if let repoOwner = RepoOwner(json: repoOwnerJSON) {
print(repoOwner.repoId)
print(repoOwner.name)
}
Gloss 也支持从 JSON 数组创建模型。可以调用 Gloss 模型数组类型的 from(jsonArray:)
函数,从传入的 JSON 数组中生成该类型的对象数组。
例如,让我们考虑以下表示仓库所有者的 JSON 数组
let repoOwnersJSON = [
[
"id" : 5456481,
"name": "hkellaway"
],
[
"id" : 1234567,
"name": "user2"
]
]
可以通过以下方式获取 RepoOwner
对象数组
guard let repoOwners = [RepoOwner].from(jsonArray: repoOwnersJSON) else {
// handle decoding failure here
}
print(repoOwners)
为了方便起见,模型对象也可以直接从 Data
初始化。
let repoOwner: RepoOwner? = RepoOwner(data: repoOwnerData)
let repoOwners: [RepoOwner]? = [RepoOwner].from(data: repoOwnerDAta)
通过 toJSON()
获取 Encodable
Gloss 模型的 JSON 表示形式。
repoOwner.toJSON()
通过 toJSONArray()
获取来自 Encodable
模型数组的 JSON 数组。
guard let jsonArray = repoOwners.toJSONArray() else {
// handle encoding failure here
}
print(jsonArray)
在前面的示例中,我们看到了 Repo
有一个嵌套模型 RepoOwner
- 并且嵌套 Gloss 模型被自动处理。但是,如果我们 JSON 中的嵌套模型果真不需要是自己的模型呢?
Gloss 通过简单的 .
语法提供了指示嵌套模型值的方式 - 让我们回顾 Repo
的 owner
值,看看有哪些变化
import Gloss
struct Repo: Glossy {
let ownerId: Int?
let ownerUsername: String?
// MARK: - Deserialization
init?(json: JSON) {
self.ownerId = "owner.id" <~~ json
self.ownerUsername = "owner.login" <~~ json
}
// MARK: - Serialization
func toJSON() -> JSON? {
return jsonify([
"owner.id" ~~> self.ownerId,
"owner.login" ~~> self.ownerUsername
])
}
现在,我们不再需要声明一个具有自己的 id
和 username
属性的嵌套模型 owner
,只需指定由点分隔的键名字符串(即 owner.id
和 owner.login
)即可检索所需值。
Gloss内置了一系列变换以便于使用(参见:术语操作符)。
NSDate
需要额外的dateFormatter
参数,因此不能通过二元操作符(<~~
和 ~~>
)来检索。
从JSON到JSON的转换是通过以下方式处理的
Decoder.decode(dateForKey:, dateFormatter:)
和 Decode.decode(dateArrayFromKey:, dateFormatter:)
,其中 key
是JSON键,dateFormatter
是用来转换日期的 DateFormatter
。例如:self.date = Decoder.decode(dateForKey: "dateKey", dateFormatter: myDateFormatter)(json)
Encoder.encode(dateForKey:, dateFormatter:)
和 Encode.encode(dateForKey:, dateFormatter:)
,其中 key
是JSON键,dateFormatter
是用来转换日期的 DateFormatter
。例如:Encoder.encode(dateForKey: "dateKey", dateFormatter: myDateFormatter)(self.date)
您可以在模型创建过程中编写自己的函数来执行自定义变换。
假设在我们的 RepoOwner
模型上,username
属性需要是一个大写字符串。我们可以更新如下
import Gloss
struct RepoOwner: Decodable {
let ownerId: Int?
let username: String?
// MARK: - Deserialization
init?(json: JSON) {
self.ownerId = "id" <~~ json
self.username = Decoder.decodeStringUppercase(key: "login", json: json)
}
}
extension Decoder {
static func decodeStringUppercase(key: String, json: JSON) -> String? {
if let string = json.valueForKeypath(key) as? String {
return string.uppercaseString
}
return nil
}
}
我们为 Decoder
创建了一个扩展并编写了自己的解码函数 decodeStringUppercase
。
需要注意的是,decodeStringUppercase
的返回类型是我们希望得到的类型--在这种情况下,是 String?
。您正在处理的价值可以通过 json.valueForKeypath(_:)
访问,并且需要使用 as?
转换成所期望的类型。然后,可以进行操作 -- 例如,转换为大写。转换后的值应该被返回;在转换失败的情况下,应返回 nil
。
虽然此处显示在同一文件中,但 Decoder
的扩展并不是必须的。此外,将自定义解码函数作为 Decoder
的成员没有必要,但只是为了符合Gloss的语义。
您也可以编写自己的函数,在JSON转换过程中进行自定义变换。
假设我们在 RepoOwner
模型上,username
属性需要是一个小写字符串。我们可以更新如下
import Gloss
struct RepoOwner: Glossy {
let ownerId: Int?
let username: String?
// MARK: - Deserialization
// ...
// MARK: - Serialization
func toJSON() -> JSON? {
return jsonify([
"id" ~~> self.ownerId,
Encoder.encodeStringLowercase(key: "login", value: self.username)
])
}
}
extension Encoder {
static func encodeStringLowercase(key: String, value: String?) -> JSON? {
if let value = value {
return [key : value.lowercaseString]
}
return nil
}
}
我们为 Encoder
创建了一个扩展并编写了自己的编码函数 encodeStringLowercase
。
需要注意的重要一点是 encodeStringLowercase
接受一个 value
,其类型是它要转换的(String?
),返回 JSON?
。您正在处理的价值可以通过 if let
语句访问。然后,可以进行操作 -- 例如,转换为小写。应返回一个字典,其 key
是键,变换后的值作为其值。如果 if let
语句失败了,应返回 nil
。
虽然此处显示在同一文件中,但 Encoder
的扩展也不是必须的。此外,将自定义编码函数作为 Encoder
的成员没有必要,但只是为了符合Gloss的语义。
提供自定义操作符的Gloss工具旨在让你的模型更简洁。然而,一些人出于正当理由选择不使用自定义操作符——自定义操作符并不总是清晰地传达其操作内容(参见这个讨论)。
如果你希望不使用<~~
或~~>
操作符,可以使用它们的互补函数Decoder.decode
和Encoder.encode
。
例如,
原来的self.url = "html_url" <~~ json
将变为self.url = Decoder.decodeURL("html_url")(json)
并且
原来的"html_url" ~~> self.url
将变为Encoder.encodeURL("html_url")(self.url)
备注:Int32、UInt32、Int64和UInt64类型,以及数组在这些类型,在Linux平台上无法使用。请使用Int、UInt和相应的数组。 - @rbukovansky
<~~
<~~
操作符简单来说是一组Decoder.decode
函数的语法糖。
Decoder.decode(key:
)Decodable
模型(Decoder.decode(decodableForKey:
)Decoder.decode(key:
)Decodable
模型的数组(Decoder.decode(decodableArrayForKey:
)Decodable
模型的字典(Decoder.decode(decodableDictionaryForKey:
)Decoder.decode(enumForKey:
)Decoder.decode(enumArrayForKey:
)Decoder.decode(int32ForKey:
)Decoder.decode(int32ArrayForKey:
)Decoder.decode(uint32ForKey:
)Decoder.decode(uint32ArrayForKey:
)Decoder.decode(int64ForKey:
)Decoder.decode(int64ArrayForKey:
)Decoder.decode(uint64ForKey:
)Decoder.decode(uint64ArrayForKey:
)Decoder.decode(urlForKey:
)Decode.decode(urlArrayForKey:
)Decoder.decode(uuidForKey:
)Decoder.decode(uuidArrayForKey:
)Decoder.decode(dedimalForKey:
)Decoder.decode(decimalArrayForKey:
)~~>
~~>
操作符简单来说是一组Encoder.encode
函数的语法糖。
Encoder.encode(key:
)Encodable
模型(Encoder.encode(encodableForKey:
)Encoder.encode(arrayForKey:
)Encodable
模型的数组(Encoder.encode(encodableArrayForKey:
)Encodable
模型的字典(Encoder.encode(encodableDictionaryForKey:
)Encoder.encode(enumForKey:
)Encoder.encode(enumArrayForKey:
)Encoder.encode(int32ForKey:
)Encoder.encode(int32ArrayForKey:
)Encoder.encode(uint32ForKey:
)Encoder.encode(uint32ArrayForKey:
)Encoder.encode(int64ForKey:
)Encoder.encode(int64ArrayForKey:
)Encoder.encode(uint64ForKey:
)Encoder.encode(uint64ArrayForKey:
)Encoder.encode(urlForKey:
)Encoder.encode(uuidForKey:
)Encoder.encode(decimalForKey:
》Encoder.encode(decimalArrayForKey:
)要从JSON创建的模型必须采用《Decodable
》协议。
要转换为JSON的模型必须采用《Encodable
》协议。
示例中提到的《Glossy
》协议只是一个方便定义可以转换为JSON及其反序列化的模型的协议。《Glossy
》可以被《Decodable
,《Encodable
》代替,以获得更高的精确度,如果需要的话。
Gloss的名字灵感来源于一个流行的Objective-C库Mantle,两者的名字都是对“层”一词的双关语,指的是它们在支持应用程序模型层方面的作用。
特别选择“gloss”这个词,因为它既代表轻量级,又增添美感。
Gloss是由Harlan Kellaway创建的。
灵感来源于其他优秀的JSON解析库,如Argo。关于为什么创建Gloss的更多详细信息,请参阅此处。
特别感谢所有贡献者!
看看Gloss在这些酷炫的地方!
在你的应用中使用Gloss?让我知道。[(邮箱)](mailto:[email protected]?subject=在我项目中使用Gloss)
Gloss遵循MIT许可证。更多信息请参阅LICENSE文件。