J 1.0.5

J 1.0.5

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

Zaid 维护。



J 1.0.5

功能

Reference Status

  • 将 JSON 映射到对象
  • 将对象映射到 JSON
  • 嵌套对象
  • 自定义转换

入门指南

  • 下载 Gloss 并在附带的 GlossExample 应用上运行 pod install 来查看 Gloss 的实际应用
  • 查看 文档,以更全面地了解 Gloss 中的可用类

Swift 2.x

使用 swift_2.3 分支来获取兼容的 Gloss 版本和示例项目。

目前通过 CocoaPods 和 Carthage 可获得的 Gloss 源代码与 Swift 3.0 兼容。

使用 Swift 包管理器安装

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)
}

从 JSON 数组创建模型对象

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)

将模型对象转换为 JSON

通过 toJSON() 获取 Encodable Gloss 模型的 JSON 表示形式。

repoOwner.toJSON()

从模型对象中获取 JSON 数组

通过 toJSONArray() 获取来自 Encodable 模型数组的 JSON 数组。

guard let jsonArray = repoOwners.toJSONArray() else {
    // handle encoding failure here
}

print(jsonArray)

不创建额外模型检索嵌套模型值

在前面的示例中,我们看到了 Repo 有一个嵌套模型 RepoOwner - 并且嵌套 Gloss 模型被自动处理。但是,如果我们 JSON 中的嵌套模型果真不需要是自己的模型呢?

Gloss 通过简单的 . 语法提供了指示嵌套模型值的方式 - 让我们回顾 Repoowner 值,看看有哪些变化

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
            ])

}

现在,我们不再需要声明一个具有自己的 idusername 属性的嵌套模型 owner,只需指定由点分隔的键名字符串(即 owner.idowner.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)

自定义变换

从JSON

您可以在模型创建过程中编写自己的函数来执行自定义变换。

假设在我们的 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

您也可以编写自己的函数,在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操作符

不使用Gloss操作符

提供自定义操作符的Gloss工具旨在让你的模型更简洁。然而,一些人出于正当理由选择不使用自定义操作符——自定义操作符并不总是清晰地传达其操作内容(参见这个讨论)。

如果你希望不使用<~~~~>操作符,可以使用它们的互补函数Decoder.decodeEncoder.encode

例如,

原来的self.url = "html_url" <~~ json将变为self.url = Decoder.decodeURL("html_url")(json)

并且

原来的"html_url" ~~> self.url将变为Encoder.encodeURL("html_url")(self.url)

关于Gloss操作符的使用

备注: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:
  • Int32类型(Decoder.decode(int32ForKey:
  • Int32数组(Decoder.decode(int32ArrayForKey:
  • UInt32类型(Decoder.decode(uint32ForKey:
  • UInt32数组(Decoder.decode(uint32ArrayForKey:
  • Int64类型(Decoder.decode(int64ForKey:
  • Int64数组(Decoder.decode(int64ArrayForKey:
  • UInt64类型(Decoder.decode(uint64ForKey:
  • UInt64数组(Decoder.decode(uint64ArrayForKey:
  • NSURL类型(Decoder.decode(urlForKey:
  • NSURL数组(Decode.decode(urlArrayForKey:
  • UUID类型(Decoder.decode(uuidForKey:
  • UUID数组(Decoder.decode(uuidArrayForKey:
  • Decimal类型(Decoder.decode(dedimalForKey:
  • Decimal数组(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:
  • Int32类型(Encoder.encode(int32ForKey:
  • Int32数组(Encoder.encode(int32ArrayForKey:
  • UInt32类型(Encoder.encode(uint32ForKey:
  • UInt32数组(Encoder.encode(uint32ArrayForKey:
  • Int64类型(Encoder.encode(int64ForKey:
  • Int64数组(Encoder.encode(int64ArrayForKey:
  • UInt64类型(Encoder.encode(uint64ForKey:
  • UInt64数组(Encoder.encode(uint64ArrayForKey:
  • NSURL类型(Encoder.encode(urlForKey:
  • UUID类型(Encoder.encode(uuidForKey:
  • Decimal类型(《Encoder.encode(decimalForKey:
  • Decimal数组(Encoder.encode(decimalArrayForKey:

Gloss协议

要从JSON创建的模型必须采用《Decodable》协议。

要转换为JSON的模型必须采用《Encodable》协议。

示例中提到的《Glossy》协议只是一个方便定义可以转换为JSON及其反序列化的模型的协议。《Glossy》可以被《Decodable,《Encodable》代替,以获得更高的精确度,如果需要的话。

为什么叫做“Gloss”?

Gloss的名字灵感来源于一个流行的Objective-C库Mantle,两者的名字都是对“层”一词的双关语,指的是它们在支持应用程序模型层方面的作用。

特别选择“gloss”这个词,因为它既代表轻量级,又增添美感。

致谢

Gloss是由Harlan Kellaway创建的。

灵感来源于其他优秀的JSON解析库,如Argo。关于为什么创建Gloss的更多详细信息,请参阅此处

特别感谢所有贡献者💖

推荐

看看Gloss在这些酷炫的地方!

文章

SDKs/产品

应用

工具

通讯

在你的应用中使用Gloss?让我知道。[(邮箱)](mailto:[email protected]?subject=在我项目中使用Gloss)

许可证

Gloss遵循MIT许可证。更多信息请参阅LICENSE文件。