ObjectMapper 文檔中文翻譯

ecx7zkzgz6 8年前發布 | 12K 次閱讀 Alamofire iOS開發 移動開發

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 添加到項目中:

  1. 打開終端,使用 cd 命令進入項目文件的根目錄下,然后在終端中輸入 git submodule add https://github.com/Hearst-DD/ObjectMapper.git ,把 ObjectMapper 作為項目的一個 submodule 添加進來。
  2. 打開 ObjectMapper 文件,并將 ObjectMapper.xcodeproj 拖進你 app 項目的文件導航中。
  3. 在 Xcode 中,文件導航中點擊藍色項目圖標進入到 target 配置界面,在側邊欄的 "TARGETS" 下選擇主工程對應的target。
  4. 確保 ObjectMapper.framework 的部署版本( deployment target )和主工程的部署版本保持一致。
  5. 在配置界面的頂部選項欄中,打開 "Build Phases" 面板。
  6. 展開 "Target Dependencies" 組,并添加 ObjectMapper.framework 。
  7. 點擊面板左上角的 + 按鈕,選擇 "New Copy Files Phase"。將這個階段重命名為 "Copy Frameworks",設置 "Destination" 為 "Frameworks",最后添加 ObjectMapper.framework 。

 

來自:https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide

 

 本文由用戶 ecx7zkzgz6 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!