ObjectMapper 文档中文翻译

ecx7zkzgz6 5年前
   <h2><strong>ObjectMapper-CN-Guide</strong></h2>    <p>ObjectMapper 是一个使用 Swift 编写的用于 model 对象(类和结构体)和 JSON 之间转换的框架。</p>    <ul>     <li>特性</li>     <li>基础使用方法</li>     <li>映射嵌套对象</li>     <li>自定义转换规则</li>     <li>继承</li>     <li>泛型对象</li>     <li>映射时的上下文对象</li>     <li>ObjectMapper + Alamofire</li>     <li>ObjectMapper + Realm</li>     <li>待完成</li>     <li>安装</li>    </ul>    <h2><strong>特性:</strong></h2>    <ul>     <li>把 JSON 映射成对象</li>     <li>把对象映射 JSON</li>     <li>支持嵌套对象 (单独的成员变量、在数组或字典中都可以)</li>     <li>在转换过程支持自定义规则</li>     <li>支持结构体( Struct )</li>     <li>Immutable support(目前还在 beta )</li>    </ul>    <h2><strong>基础使用方法</strong></h2>    <p>为了支持映射,类或者结构体只需要实现 Mappable 协议。这个协议包含以下方法:</p>    <pre>  <code class="language-swift">init?(map: Map)  mutating func mapping(map: Map)</code></pre>    <p>ObjectMapper使用自定义的 <- 运算符来声明成员变量和 JSON 的映射关系。</p>    <pre>  <code class="language-swift">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>    <p>一旦你的对象实现了 Mappable , ObjectMapper就可以让你轻松的实现和 JSON 之间的转换。</p>    <p>把 JSON 字符串转成 model 对象:</p>    <pre>  <code class="language-swift">let user = User(JSONString: JSONString)</code></pre>    <p>把一个 model 转成 JSON 字符串:</p>    <pre>  <code class="language-swift">let JSONString = user.toJSONString(prettyPrint: true)</code></pre>    <p>也可以使用 Mapper.swift 类来完成转换(这个类还额外提供了一些函数来处理一些特殊的情况:</p>    <pre>  <code class="language-swift">// 把 JSON 字符串转成 Model  let user = Mapper<User>().map(JSONString: JSONString)  // 根据 Model 生成 JSON 字符串  let JSONString = Mapper().toJSONString(user, prettyPrint: true)</code></pre>    <p>ObjectMapper支持以下的类型映射到对象中:</p>    <ul>     <li>Int</li>     <li>Bool</li>     <li>Double</li>     <li>Float</li>     <li>String</li>     <li>RawRepresentable (枚举)</li>     <li>Array<AnyObject></li>     <li>Dictionary<String, AnyObject></li>     <li>Object<T: Mappable></li>     <li>Array<T: Mappable></li>     <li>Array<Array<T: Mappable>></li>     <li>Set<T: Mappable></li>     <li>Dictionary<String, T: Mappable></li>     <li>Dictionary<String, Array<T: Mappable>></li>     <li>以上所有的 Optional 类型</li>     <li>以上所有的隐式强制解包类型(Implicitly Unwrapped Optional)</li>    </ul>    <h2><strong>Mappable 协议</strong></h2>    <p>mutating func mapping(map: Map)</p>    <p>所有的映射最后都会调用到这个函数。当解析 JSON 时,这个函数会在对象创建成功后被执行。当生成 JSON 时就只有这个函数会被对象调用。</p>    <p>init?(map: Map)</p>    <p>这个可失败的初始化函数是 ObjectMapper 创建对象的时候使用的。开发者可以通过这个函数在映射前校验 JSON 。如果在这个方法里返回 nil 就不会执行 mapping 函数。可以通过传入的保存着 JSON 的 Map 对象进行校验:</p>    <pre>  <code class="language-swift">required init?(map: Map){      // 检查 JSON 里是否有一定要有的 "name" 属性      if map.JSONDictionary["name"] == nil {          return nil      }  }</code></pre>    <h2><strong>StaticMappable 协议</strong></h2>    <p>StaticMappable 是 Mappable 之外的另一种选择。 这个协议可以让开发者通过一个静态函数初始化对象而不是通过 init?(map: Map) 。</p>    <p>注意: StaticMappable 和 Mappable 都继承了 BaseMappable 协议。 BaseMappable 协议声明了 mapping(map: Map) 函数。</p>    <p>static func objectForMapping(map: Map) -> BaseMappable?</p>    <p>ObjectMapper 使用这个函数获取对象后进行映射。开发者需要在这个函数里返回一个实现 BaseMappable 对象的实例。这个函数也可以用于:</p>    <ul>     <li>在对象进行映射前校验 JSON</li>     <li>提供一个缓存过的对象用于映射</li>     <li>返回另外一种类型的对象(当然是必须实现了 BaseMappable)用于映射。比如你可能通过检查 JSON 推断出用于映射的对象 (看这个例子)。</li>    </ul>    <p>如果你需要在 extension 里实现 ObjectMapper,你需要选择这个协议而不是 Mappable 。</p>    <h2><strong>ImmutableMappable Protocol (Beta)</strong></h2>    <p>:warning: 这个特性还处于 Beta 阶段。正式发布时 API 可能会完全不同。</p>    <p>使用 ImmutableMappable 可以映射不可变的属性。下面的表格展示了 ImmutableMappable 和 Mappable 的不同:</p>    <table>     <tbody>      <tr>       <th>ImmutableMappable</th>       <th>Mappable</th>      </tr>      <tr>       <th colspan="2">Properties</th>      </tr>      <tr>       <td> <pre>  <code class="language-swift"><strong>let</strong> id: Int  <strong>let</strong> name: String?</code></pre> </td>       <td> <pre>  <code class="language-swift">var id: Int!  var name: String?</code></pre> </td>      </tr>      <tr>       <th colspan="2">JSON -> Model</th>      </tr>      <tr>       <td> <pre>  <code class="language-swift">init(map: Map) <strong>throws</strong> {    id   = <strong>try</strong> map.value("id")    name = <strong>try?</strong> map.value("name")  }</code></pre> </td>       <td> <pre>  <code class="language-swift">mutating func mapping(map: Map) {    id   <- map["id"]    name <- map["name"]  }</code></pre> </td>      </tr>      <tr>       <th colspan="2">Model -> JSON</th>      </tr>      <tr>       <td> <pre>  <code class="language-swift">mutating func mapping(map: Map) {    id   <strong>>>></strong> map["id"]    name <strong>>>></strong> map["name"]  }</code></pre> </td>       <td> <pre>  <code class="language-swift">mutating func mapping(map: Map) {    id   <- map["id"]    name <- map["name"]  }</code></pre> </td>      </tr>      <tr>       <th colspan="2">Initializing</th>      </tr>      <tr>       <td> <pre>  <code class="language-swift"><strong>try</strong> User(JSONString: JSONString)</code></pre> </td>       <td> <pre>  <code class="language-swift">User(JSONString: JSONString)</code></pre> </td>      </tr>     </tbody>    </table>    <p>init(map: Map) throws</p>    <p>这个可能抛出异常的初始化函数用于在提供的 Map 里映射不可变属性。每个不可变的初始化属性都要在这个初始化函数里初始化。</p>    <p>当发生下列情况时初始化函数会抛出一个错误:</p>    <ul>     <li>Map 根据提供的键名获取不到对应值</li>     <li>Map 使用 Transform 后没有得到值</li>    </ul>    <p>ImmutableMappable 使用 Map.value(_:using:) 方法从 Map 中获取值。因为可能抛出异常,这个方法在使用时需要使用 try 关键字。 Optional 的属性可以简单的用 try? 处理。</p>    <pre>  <code class="language-swift">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  }</code></pre>    <p>mutating func mapping(map: Map)</p>    <p>这个方法是在 Model 转回 JSON 时调用的。因为不可变的属性不能被 <- 映射,所以映射回来时需要使用 >>> 。</p>    <pre>  <code class="language-swift">mutating func mapping(map: Map) {      name      >>> map["name"]      createdAt >>> (map["createdAt"], DateTransform())      updatedAt >>> (map["updatedAt"], DateTransform())      posts     >>> map["posts"]  }</code></pre>    <h2><strong>轻松映射嵌套对象</strong></h2>    <p>ObjectMapper 支持使用点语法来轻松实现嵌套对象的映射。比如有如下的 JSON 字符串:</p>    <pre>  <code class="language-swift">"distance" : {       "text" : "102 ft",       "value" : 31  }</code></pre>    <p>你可以通过这种写法直接访问到嵌套对象:</p>    <pre>  <code class="language-swift">func mapping(map: Map) {      distance <- map["distance.value"]  }</code></pre>    <p>嵌套的键名也支持访问数组中的值。如果有一个返回的 JSON 是一个包含 distance 的数组,可以通过这种写法访问:</p>    <pre>  <code class="language-swift">distance <- map["distances.0.value"]</code></pre>    <p>如果你的键名刚好含有 . 符号,你需要特别声明关闭上面提到的获取嵌套对象功能:</p>    <pre>  <code class="language-swift">func mapping(map: Map) {      identifier <- map["app.identifier", nested: false]  }</code></pre>    <p>如果刚好有嵌套的对象的键名还有 . ,可以在中间加入一个自定义的分割符(#629):</p>    <pre>  <code class="language-swift">func mapping(map: Map) {      appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"]  }</code></pre>    <p>这种情况的 JSON 是这样的:</p>    <pre>  <code class="language-swift">"com.myapp.info" : {       "com.myapp.name" : "SwiftOldDriver"  }</code></pre>    <h2><strong>自定义转换规则</strong></h2>    <p>ObjectMapper 也支持在映射时自定义转换规则。如果要使用自定义转换,创建一个 tuple(元祖)包含 map["field_name"] 和你要使用的变换放在 <- 的右边:</p>    <pre>  <code class="language-swift">birthday <- (map["birthday"], DateTransform())</code></pre>    <p>当解析 JSON 时上面的转换会把 JSON 里面的 Int 值转成一个 NSDate ,如果是对象转为 JSON 时,则会把 NSDate 对象转成 Int 值。</p>    <p>只要实现 TransformType 协议就可以轻松的创建自定义的转换规则:</p>    <pre>  <code class="language-swift">public protocol TransformType {      associatedtype Object      associatedtype JSON        func transformFromJSON(_ value: Any?) -> Object?      func transformToJSON(_ value: Object?) -> JSON?  }</code></pre>    <h3><strong>TransformOf</strong></h3>    <p>大多数情况下你都可以使用框架提供的转换类 TransformOf 来快速的实现一个期望的转换。 TransformOf 的初始化需要两个类型和两个闭包。两个类型声明了转换的目标类型和源类型,闭包则实现具体转换逻辑。</p>    <p>举个例子,如果你想要把一个 JSON 字符串转成 Int ,你可以像这样使用 TransformOf :</p>    <pre>  <code class="language-swift">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>    <p>这是一种更省略的写法:</p>    <pre>  <code class="language-swift">id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))</code></pre>    <h2><strong>继承</strong></h2>    <p>实现了 Mappable 协议的类可以容易的被继承。当继承一个 mappable 的类时,使用这样的结构:</p>    <pre>  <code class="language-swift">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>    <p>注意确认子类中的实现调用了父类中正确的初始化器和映射函数。</p>    <h2><strong>泛型对象</strong></h2>    <p>ObjectMapper 可以处理泛型只要这个泛型也实现了 Mappable 协议。看这个例子:</p>    <pre>  <code class="language-swift">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>    <h2><strong>映射时的上下文对象</strong></h2>    <p>Map 是在映射时传入的对象,带有一个 optional MapContext 对象,开发者可以通过使用这个对象在映射时传入一些信息。</p>    <p>为了使用这个特性,需要先创建一个对象实现了 MapContext 协议(这个协议是空的),然后在初始化时传入 Mapper 中。</p>    <pre>  <code class="language-swift">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>    <h2><strong>ObjectMapper + Alamofire</strong></h2>    <p>如果网络层你使用的是Alamofire ,并且你希望把返回的结果转换成 Swift 对象,你可以使用 AlamofireObjectMapper 。这是一个使用 ObjectMapper 实现的把返回的 JSON 自动转成 Swift 对象的 Alamofire 的扩展。</p>    <h2><strong>ObjectMapper + Realm</strong></h2>    <p>ObjectMapper 可以和 Realm 一起配合使用。使用下面的声明结构就可以使用 ObjectMapper 生成 Realm 对象:</p>    <pre>  <code class="language-swift">class Model: Object, Mappable {      dynamic var name = ""        required convenience init?(map: Map) {          self.init()      }        func mapping(map: Map) {          name <- map["name"]      }  }</code></pre>    <p>如果你想要序列化相关联的 RealmObject,你可以使用ObjectMapper+Realm。这是一个简单的 Realm 扩展,用于把任意的 JSON 序列化成 Realm 的类(ealm's List class。)</p>    <p>注意:使用 ObjectMappers 的 toJSON 函数来生成 JSON 字符串只在 Realm 的写事务中有效(write transaction)。这是因为 ObjectMapper 在解析和生成时在映射函数( <- )中使用 inout 作为标记( flag )。Realm 会检测到标记并且强制要求 toJSON 函数只能在一个写的事务中调用,即使这个对象并没有被修改。</p>    <h2><strong>待完成</strong></h2>    <ul>     <li>改善错误的处理。可能使用 throws 来处理。</li>     <li>相关类的文档完善</li>    </ul>    <h2><strong>安装</strong></h2>    <h3><strong>Cocoapods</strong></h3>    <p>如果你的项目使用 CocoaPods 0.36 及以上 的版本,你可以把下面内容添加到在 Podfile 中,将 ObjectMapper 添加到你的项目中:</p>    <pre>  <code class="language-swift">pod 'ObjectMapper', '~> 2.2'</code></pre>    <h3><strong>Carthage</strong></h3>    <p>如果你的项目使用Carthage ,你可以把下面的内容添加到 Cartfile 中,将 ObjectMapper 的依赖到你的项目中:</p>    <pre>  <code class="language-swift">github "Hearst-DD/ObjectMapper" ~> 2.2</code></pre>    <h3><strong>Swift Package Manager</strong></h3>    <p>如果你的项目使用 Swift Package Manager ,那么你可以把下面内容添加到 Package.swift 中的 dependencies 数组中,将 ObjectMapper 的依赖到你的项目中:</p>    <pre>  <code class="language-swift">.Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2),</code></pre>    <h3><strong>Submodule</strong></h3>    <p>此外,ObjectMapper 也可以作为一个 submodule 添加到项目中:</p>    <ol>     <li>打开终端,使用 cd 命令进入项目文件的根目录下,然后在终端中输入 git submodule add https://github.com/Hearst-DD/ObjectMapper.git ,把 ObjectMapper 作为项目的一个 submodule 添加进来。</li>     <li>打开 ObjectMapper 文件,并将 ObjectMapper.xcodeproj 拖进你 app 项目的文件导航中。</li>     <li>在 Xcode 中,文件导航中点击蓝色项目图标进入到 target 配置界面,在侧边栏的 "TARGETS" 下选择主工程对应的target。</li>     <li>确保 ObjectMapper.framework 的部署版本( deployment target )和主工程的部署版本保持一致。</li>     <li>在配置界面的顶部选项栏中,打开 "Build Phases" 面板。</li>     <li>展开 "Target Dependencies" 组,并添加 ObjectMapper.framework 。</li>     <li>点击面板左上角的 + 按钮,选择 "New Copy Files Phase"。将这个阶段重命名为 "Copy Frameworks",设置 "Destination" 为 "Frameworks",最后添加 ObjectMapper.framework 。</li>    </ol>    <p> </p>    <p>来自:https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide</p>    <p> </p>