ObjectMapper 文檔中文翻譯
ObjectMapper-CN-Guide
ObjectMapper 是一個使用 Swift 編寫的用于 model 對象(類和結構體)和 JSON 之間轉換的框架。
- 特性
- 基礎使用方法
- 映射嵌套對象
- 自定義轉換規則
- 繼承
- 泛型對象
- 映射時的上下文對象
- ObjectMapper + Alamofire
- ObjectMapper + Realm
- 待完成
- 安裝
特性:
- 把 JSON 映射成對象
- 把對象映射 JSON
- 支持嵌套對象 (單獨的成員變量、在數組或字典中都可以)
- 在轉換過程支持自定義規則
- 支持結構體( Struct )
- Immutable support(目前還在 beta )
基礎使用方法
為了支持映射,類或者結構體只需要實現 Mappable 協議。這個協議包含以下方法:
init?(map: Map)
mutating func mapping(map: Map)
ObjectMapper使用自定義的 <- 運算符來聲明成員變量和 JSON 的映射關系。
class User: Mappable {
var username: String?
var age: Int?
var weight: Double!
var array: [AnyObject]?
var dictionary: [String : AnyObject] = [:]
var bestFriend: User? // 嵌套的 User 對象
var friends: [User]? // Users 的數組
var birthday: NSDate?
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"]
}
}</code></pre>
一旦你的對象實現了 Mappable , ObjectMapper就可以讓你輕松的實現和 JSON 之間的轉換。
把 JSON 字符串轉成 model 對象:
let user = User(JSONString: JSONString)
把一個 model 轉成 JSON 字符串:
let JSONString = user.toJSONString(prettyPrint: true)
也可以使用 Mapper.swift 類來完成轉換(這個類還額外提供了一些函數來處理一些特殊的情況:
// 把 JSON 字符串轉成 Model
let user = Mapper<User>().map(JSONString: JSONString)
// 根據 Model 生成 JSON 字符串
let JSONString = Mapper().toJSONString(user, prettyPrint: true)
ObjectMapper支持以下的類型映射到對象中:
- Int
- Bool
- Double
- Float
- String
- RawRepresentable (枚舉)
- Array<AnyObject>
- Dictionary<String, AnyObject>
- Object<T: Mappable>
- Array<T: Mappable>
- Array<Array<T: Mappable>>
- Set<T: Mappable>
- Dictionary<String, T: Mappable>
- Dictionary<String, Array<T: Mappable>>
- 以上所有的 Optional 類型
- 以上所有的隱式強制解包類型(Implicitly Unwrapped Optional)
Mappable 協議
mutating func mapping(map: Map)
所有的映射最后都會調用到這個函數。當解析 JSON 時,這個函數會在對象創建成功后被執行。當生成 JSON 時就只有這個函數會被對象調用。
init?(map: Map)
這個可失敗的初始化函數是 ObjectMapper 創建對象的時候使用的。開發者可以通過這個函數在映射前校驗 JSON 。如果在這個方法里返回 nil 就不會執行 mapping 函數。可以通過傳入的保存著 JSON 的 Map 對象進行校驗:
required init?(map: Map){
// 檢查 JSON 里是否有一定要有的 "name" 屬性
if map.JSONDictionary["name"] == nil {
return nil
}
}
StaticMappable 協議
StaticMappable 是 Mappable 之外的另一種選擇。 這個協議可以讓開發者通過一個靜態函數初始化對象而不是通過 init?(map: Map) 。
注意: StaticMappable 和 Mappable 都繼承了 BaseMappable 協議。 BaseMappable 協議聲明了 mapping(map: Map) 函數。
static func objectForMapping(map: Map) -> BaseMappable?
ObjectMapper 使用這個函數獲取對象后進行映射。開發者需要在這個函數里返回一個實現 BaseMappable 對象的實例。這個函數也可以用于:
- 在對象進行映射前校驗 JSON
- 提供一個緩存過的對象用于映射
- 返回另外一種類型的對象(當然是必須實現了 BaseMappable)用于映射。比如你可能通過檢查 JSON 推斷出用于映射的對象 (看這個例子)。
如果你需要在 extension 里實現 ObjectMapper,你需要選擇這個協議而不是 Mappable 。
ImmutableMappable Protocol (Beta)
:warning: 這個特性還處于 Beta 階段。正式發布時 API 可能會完全不同。
使用 ImmutableMappable 可以映射不可變的屬性。下面的表格展示了 ImmutableMappable 和 Mappable 的不同:
ImmutableMappable
Mappable
Properties
let id: Int
let name: String?
var id: Int!
var name: String?
JSON -> Model
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"]
}
Model -> JSON
mutating func mapping(map: Map) {
id >>> map["id"]
name >>> map["name"]
}
mutating func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
}
Initializing
try User(JSONString: JSONString)
User(JSONString: JSONString)
init(map: Map) throws
這個可能拋出異常的初始化函數用于在提供的 Map 里映射不可變屬性。每個不可變的初始化屬性都要在這個初始化函數里初始化。
當發生下列情況時初始化函數會拋出一個錯誤:
- Map 根據提供的鍵名獲取不到對應值
- Map 使用 Transform 后沒有得到值
ImmutableMappable 使用 Map.value(_:using:) 方法從 Map 中獲取值。因為可能拋出異常,這個方法在使用時需要使用 try 關鍵字。 Optional 的屬性可以簡單的用 try? 處理。
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
}
mutating func mapping(map: Map)
這個方法是在 Model 轉回 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 的數組,可以通過這種寫法訪問:
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: "->"]
}
這種情況的 JSON 是這樣的:
"com.myapp.info" : {
"com.myapp.name" : "SwiftOldDriver"
}
自定義轉換規則
ObjectMapper 也支持在映射時自定義轉換規則。如果要使用自定義轉換,創建一個 tuple(元祖)包含 map["field_name"] 和你要使用的變換放在 <- 的右邊:
birthday <- (map["birthday"], DateTransform())
當解析 JSON 時上面的轉換會把 JSON 里面的 Int 值轉成一個 NSDate ,如果是對象轉為 JSON 時,則會把 NSDate 對象轉成 Int 值。
只要實現 TransformType 協議就可以輕松的創建自定義的轉換規則:
public protocol TransformType {
associatedtype Object
associatedtype JSON
func transformFromJSON(_ value: Any?) -> Object?
func transformToJSON(_ value: Object?) -> JSON?
}</code></pre>
TransformOf
大多數情況下你都可以使用框架提供的轉換類 TransformOf 來快速的實現一個期望的轉換。 TransformOf 的初始化需要兩個類型和兩個閉包。兩個類型聲明了轉換的目標類型和源類型,閉包則實現具體轉換邏輯。
舉個例子,如果你想要把一個 JSON 字符串轉成 Int ,你可以像這樣使用 TransformOf :
let transform = TransformOf<Int, String>(fromJSON: { (value: String?) -> Int? in
// 把值從 String? 轉成 Int?
return Int(value!)
}, toJSON: { (value: Int?) -> String? in
// 把值從 Int? 轉成 String?
if let value = value {
return String(value)
}
return nil
})
id <- (map["id"], transform)</code></pre>
這是一種更省略的寫法:
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
繼承
實現了 Mappable 協議的類可以容易的被繼承。當繼承一個 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"]
}
}</code></pre>
注意確認子類中的實現調用了父類中正確的初始化器和映射函數。
泛型對象
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)</code></pre>
映射時的上下文對象
Map 是在映射時傳入的對象,帶有一個 optional MapContext 對象,開發者可以通過使用這個對象在映射時傳入一些信息。
為了使用這個特性,需要先創建一個對象實現了 MapContext 協議(這個協議是空的),然后在初始化時傳入 Mapper 中。
struct Context: MapContext {
var importantMappingInfo = "映射時需要知道的額外信息"
}
class User: Mappable {
var name: String?
required init?(map: Map){
}
func mapping(map: Map){
if let context = map.context as? Context {
// 獲取到額外的信息
}
}
}
let context = Context()
let user = Mapper<User>(context: context).map(JSONString)</code></pre>
ObjectMapper + Alamofire
如果網絡層你使用的是Alamofire ,并且你希望把返回的結果轉換成 Swift 對象,你可以使用 AlamofireObjectMapper 。這是一個使用 ObjectMapper 實現的把返回的 JSON 自動轉成 Swift 對象的 Alamofire 的擴展。
ObjectMapper + Realm
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"]
}
}</code></pre>
如果你想要序列化相關聯的 RealmObject,你可以使用ObjectMapper+Realm。這是一個簡單的 Realm 擴展,用于把任意的 JSON 序列化成 Realm 的類(ealm's List class。)
注意:使用 ObjectMappers 的 toJSON 函數來生成 JSON 字符串只在 Realm 的寫事務中有效(write transaction)。這是因為 ObjectMapper 在解析和生成時在映射函數( <- )中使用 inout 作為標記( flag )。Realm 會檢測到標記并且強制要求 toJSON 函數只能在一個寫的事務中調用,即使這個對象并沒有被修改。
待完成
- 改善錯誤的處理。可能使用 throws 來處理。
- 相關類的文檔完善
安裝
Cocoapods
如果你的項目使用 CocoaPods 0.36 及以上 的版本,你可以把下面內容添加到在 Podfile 中,將 ObjectMapper 添加到你的項目中:
pod 'ObjectMapper', '~> 2.2'
Carthage
如果你的項目使用Carthage ,你可以把下面的內容添加到 Cartfile 中,將 ObjectMapper 的依賴到你的項目中:
github "Hearst-DD/ObjectMapper" ~> 2.2
Swift Package Manager
如果你的項目使用 Swift Package Manager ,那么你可以把下面內容添加到 Package.swift 中的 dependencies 數組中,將 ObjectMapper 的依賴到你的項目中:
.Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2),
Submodule
此外,ObjectMapper 也可以作為一個 submodule 添加到項目中:
- 打開終端,使用 cd 命令進入項目文件的根目錄下,然后在終端中輸入 git submodule add https://github.com/Hearst-DD/ObjectMapper.git ,把 ObjectMapper 作為項目的一個 submodule 添加進來。
- 打開 ObjectMapper 文件,并將 ObjectMapper.xcodeproj 拖進你 app 項目的文件導航中。
- 在 Xcode 中,文件導航中點擊藍色項目圖標進入到 target 配置界面,在側邊欄的 "TARGETS" 下選擇主工程對應的target。
- 確保 ObjectMapper.framework 的部署版本( deployment target )和主工程的部署版本保持一致。
- 在配置界面的頂部選項欄中,打開 "Build Phases" 面板。
- 展開 "Target Dependencies" 組,并添加 ObjectMapper.framework 。
- 點擊面板左上角的 + 按鈕,選擇 "New Copy Files Phase"。將這個階段重命名為 "Copy Frameworks",設置 "Destination" 為 "Frameworks",最后添加 ObjectMapper.framework 。
來自:https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide