ObjectMapper 是一个用 Swift 编写的框架,它可以让您轻松地将模型对象(类和结构体)转换为 JSON,以及从 JSON 转换回来。
为了支持映射,类或结构体只需要实现 Mappable
协议,该协议包含以下函数
init?(map: Map)
mutating func mapping(map: Map)
ObjectMapper 使用 <-
运算符来定义每个成员变量如何映射到 JSON 以及从 JSON 映射。
class User: Mappable {
var username: String?
var age: Int?
var weight: Double!
var array: [Any]?
var dictionary: [String : Any] = [:]
var bestFriend: User? // Nested User object
var friends: [User]? // Array of Users
var birthday: Date?
required init?(map: Map) {
}
// Mappable
func mapping(map: Map) {
username <- map["username"]
age <- map["age"]
weight <- map["weight"]
array <- map["arr"]
dictionary <- map["dict"]
bestFriend <- map["best_friend"]
friends <- map["friends"]
birthday <- (map["birthday"], DateTransform())
}
}
struct Temperature: Mappable {
var celsius: Double?
var fahrenheit: Double?
init?(map: Map) {
}
mutating func mapping(map: Map) {
celsius <- map["celsius"]
fahrenheit <- map["fahrenheit"]
}
}
一旦您的类实现了 Mappable
,ObjectMapper 就可以让您轻松地转换为 JSON 以及从 JSON 转换。
将 JSON 字符串转换为模型对象
let user = User(JSONString: JSONString)
将模型对象转换为 JSON 字符串
let JSONString = user.toJSONString(prettyPrint: true)
或者,也可以使用 Mapper.swift
类来完成上述操作(它还为其他情况提供了额外的功能)
// Convert JSON String to Model
let user = Mapper<User>().map(JSONString: JSONString)
// Create JSON String from Model
let JSONString = Mapper().toJSONString(user, prettyPrint: true)
ObjectMapper 可以映射由以下类型组成的类
Int
Bool
Double
Float
String
RawRepresentable
(枚举)Array<Any>
Dictionary<String, Any>
Object<T: Mappable>
Array<T: Mappable>
Array<Array<T: Mappable>>
Set<T: Mappable>
Dictionary<String, T: Mappable>
Dictionary<String, Array<T: Mappable>>
此函数是所有映射定义应放置的位置。当解析 JSON 时,此函数在成功创建对象后执行。当生成 JSON 时,它是对象上唯一被调用的函数。
ObjectMapper 使用此可失败的初始化器来创建对象。开发人员可以使用它在对象序列化之前验证 JSON。在该函数中返回 nil 将阻止映射发生。您可以检查存储在 Map
对象中的 JSON 以进行验证
required init?(map: Map){
// check if a required "name" property exists within the JSON.
if map.JSON["name"] == nil {
return nil
}
}
StaticMappable
是 Mappable
的替代方案。它为开发人员提供了一个静态函数,ObjectMapper 使用该函数进行对象初始化,而不是 init?(map: Map)
。
注意:StaticMappable
,就像 Mappable
一样,是 BaseMappable
的子协议,mapping(map: Map)
函数在 BaseMappable
中定义。
ObjectMapper 使用此函数来获取用于映射的对象。开发人员应在此函数中返回符合 BaseMappable
的对象实例。此函数还可以用于
BaseMappable
)以用于映射。例如,您可以检查 JSON 以推断应用于映射的对象类型(请参阅 ClassClusterTests.swift 中的示例)如果您需要在扩展中实现 ObjectMapper,则需要采用此协议而不是 Mappable
。
ImmutableMappable
提供了映射不可变属性的功能。这是 ImmutableMappable
与 Mappable
的不同之处
ImmutableMappable | Mappable |
---|---|
属性 | |
let id: Int let name: String? |
var id: Int! var name: String? |
JSON -> 模型 | |
init(map: Map) throws { id = try map.value("id") name = try? map.value("name") } |
mutating func mapping(map: Map) { id <- map["id"] name <- map["name"] } |
模型 -> JSON | |
func mapping(map: Map) { id >>> map["id"] name >>> map["name"] } |
mutating func mapping(map: Map) { id <- map["id"] name <- map["name"] } |
初始化 | |
try User(JSONString: JSONString) |
User(JSONString: JSONString) |
此可抛出的初始化器用于从给定的 Map
映射不可变属性。每个不可变属性都应在此初始化器中初始化。
当以下情况时,此初始化器会抛出错误
Map
无法获取给定键的值Map
无法使用 Transform
转换值ImmutableMappable
使用 Map.value(_:using:)
方法从 Map
获取值。此方法应与 try
关键字一起使用,因为它会抛出错误。可以使用 try?
轻松处理 Optional
属性。
init(map: Map) throws {
name = try map.value("name") // throws an error when it fails
createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails
updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional
posts = (try? map.value("posts")) ?? [] // optional + default value
surname = try? map.value("surname", default: "DefaultSurname") // optional + default value as an argument
}
此方法是执行反向转换(模型到 JSON)的位置。由于不可变属性无法使用 <-
运算符映射,因此开发人员必须使用 >>>
运算符定义反向转换。
mutating func mapping(map: Map) {
name >>> map["name"]
createdAt >>> (map["createdAt"], DateTransform())
updatedAt >>> (map["updatedAt"], DateTransform())
posts >>> map["posts"]
}
ObjectMapper 支持键内的点表示法,以便轻松映射嵌套对象。给定以下 JSON 字符串
"distance" : {
"text" : "102 ft",
"value" : 31
}
您可以按如下方式访问嵌套对象
func mapping(map: Map) {
distance <- map["distance.value"]
}
嵌套键还支持从数组访问值。给定一个包含距离数组的 JSON 响应,可以按如下方式访问该值
distance <- map["distances.0.value"]
如果您的键包含 .
,您可以按如下方式单独禁用上述功能
func mapping(map: Map) {
identifier <- map["app.identifier", nested: false]
}
当您有包含 .
的嵌套键时,您可以传递自定义嵌套键分隔符,如下所示 (#629)
func mapping(map: Map) {
appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"]
}
ObjectMapper 还支持自定义转换,这些转换在映射过程中转换值。要使用转换,只需在 <-
运算符的右侧创建一个包含 map["field_name"]
和您选择的转换的元组
birthday <- (map["birthday"], DateTransform())
上述转换将在读取 JSON 时将 JSON Int 值转换为 Date,并在将对象转换为 JSON 时将 Date 转换为 Int。
您可以通过采用并实现 TransformType
协议中的方法来轻松创建自己的自定义转换
public protocol TransformType {
associatedtype Object
associatedtype JSON
func transformFromJSON(_ value: Any?) -> Object?
func transformToJSON(_ value: Object?) -> JSON?
}
在许多情况下,您可以使用内置转换类 TransformOf
来快速执行所需的转换。TransformOf
使用两种类型和两个闭包进行初始化。这些类型定义了转换的来源和目标,而闭包执行实际的转换。
例如,如果您想将 JSON String
值转换为 Int
,您可以按如下方式使用 TransformOf
let transform = TransformOf<Int, String>(fromJSON: { (value: String?) -> Int? in
// transform value from String? to Int?
return Int(value!)
}, toJSON: { (value: Int?) -> String? in
// transform value from Int? to String?
if let value = value {
return String(value)
}
return nil
})
id <- (map["id"], transform)
以下是上述内容的更简洁版本
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
实现 Mappable
协议的类可以轻松地进行子类化。当子类化可映射类时,请遵循以下结构
class Base: Mappable {
var base: String?
required init?(map: Map) {
}
func mapping(map: Map) {
base <- map["base"]
}
}
class Subclass: Base {
var sub: String?
required init?(map: Map) {
super.init(map)
}
override func mapping(map: Map) {
super.mapping(map)
sub <- map["sub"]
}
}
确保您的子类实现调用正确的初始化器和映射函数,以同时应用来自超类的映射。
ObjectMapper 可以处理具有泛型类型的类,只要泛型类型也符合 Mappable
。请参阅以下示例
class Result<T: Mappable>: Mappable {
var result: T?
required init?(map: Map){
}
func mapping(map: Map) {
result <- map["result"]
}
}
let result = Mapper<Result<User>>().map(JSON)
在映射期间传递的 Map
对象具有一个可选的 MapContext
对象,开发人员可以在需要在映射期间传递信息时使用它。
要利用此功能,只需创建一个实现 MapContext
的对象(这是一个空协议),并在初始化期间将其传递给 Mapper
。
struct Context: MapContext {
var importantMappingInfo = "Info that I need during mapping"
}
class User: Mappable {
var name: String?
required init?(map: Map){
}
func mapping(map: Map){
if let context = map.context as? Context {
// use context to make decisions about mapping
}
}
}
let context = Context()
let user = Mapper<User>(context: context).map(JSONString)
如果您正在使用 Alamofire 进行网络连接,并且想要将您的响应转换为 Swift 对象,则可以使用 AlamofireObjectMapper。这是一个简单的 Alamofire 扩展,它使用 ObjectMapper 自动将 JSON 响应数据映射到 Swift 对象。
ObjectMapper 和 Realm 可以一起使用。只需遵循以下类结构,您就可以使用 ObjectMapper 生成您的 Realm 模型
class Model: Object, Mappable {
dynamic var name = ""
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
name <- map["name"]
}
}
如果您想序列化关联的 RealmObject,可以使用 ObjectMapper+Realm。这是一个简单的 Realm 扩展,它将任意 JSON 序列化到 Realm 的 List
类中。
要序列化 Swift String
、Int
、Double
和 Bool
数组,您可以使用 ObjectMapperAdditions/Realm。它会将 Swift 类型包装到可以存储在 Realm 的 List
类中的 RealmValues 中。
注意:使用 ObjectMapper 的 toJSON
函数生成 Realm 对象的 JSON 字符串仅在 Realm 写入事务中有效。这是因为 ObjectMapper 在其映射函数 (<-
) 中使用了 inout
标志,该标志既用于序列化又用于反序列化。Realm 检测到该标志,并强制 toJSON
函数在写入块内调用,即使对象未被修改。
如果您有项目利用、扩展或为 ObjectMapper 提供工具,请提交 PR,并在 README 的此部分中包含指向您项目的链接。
throws
非常欢迎贡献 👍😃。
在提交任何拉取请求之前,请确保您已运行包含的测试并且它们已通过。如果您要包含新功能,也请为其编写测试用例。
可以使用 CocoaPods 0.36 或更高版本 将 ObjectMapper 添加到您的项目中,方法是将以下行添加到您的 Podfile
中
pod 'ObjectMapper', '~> 3.5' (check releases to make sure this is the latest version)
如果您正在使用 Carthage,则可以通过将其添加到您的 Cartfile
中来添加对 ObjectMapper 的依赖
github "tristanhimmelman/ObjectMapper" ~> 3.5 (check releases to make sure this is the latest version)
要将 ObjectMapper 添加到基于 Swift Package Manager 的项目中,请添加
.package(url: "https://github.com/tristanhimmelman/ObjectMapper.git", .upToNextMajor(from: "4.1.0")),
到您的 Package.swift
文件中的 dependencies
数组。
否则,可以将 ObjectMapper 添加为子模块
cd
进入您的顶级项目目录,然后输入命令 git submodule add https://github.com/tristanhimmelman/ObjectMapper.git
,将 ObjectMapper 添加为 子模块ObjectMapper
文件夹,并将 ObjectMapper.xcodeproj
拖到您的应用程序项目的文件导航器中。ObjectMapper.framework
的部署目标与应用程序目标的部署目标相匹配。ObjectMapper.framework
。+
按钮,然后选择“New Copy Files Phase”。将这个新阶段重命名为“Copy Frameworks”,将“Destination”设置为“Frameworks”,并添加 ObjectMapper.framework
。