You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

502 lines
16 KiB

  1. # ObjectMapper-CN-Guide
  2. > 文档由Swift老司机活动中心负责翻译,欢迎关注[@SwiftOldDriver](http://weibo.com/6062089411)。翻译有问题可以到 [ObjectMapper-CN-Guide](https://github.com/SwiftOldDriver/ObjectMapper-CN-Guide) 提 PR。
  3. [ObjectMapper](https://github.com/Hearst-DD/ObjectMapper) 是一个使用 Swift 编写的用于 model 对象(类和结构体)和 JSON 之间转换的框架。
  4. - [特性](#特性)
  5. - [基础使用方法](#基础使用方法)
  6. - [映射嵌套对象](#映射嵌套对象)
  7. - [自定义转换规则](#自定义转换规则)
  8. - [继承](#继承)
  9. - [泛型对象](#泛型对象)
  10. - [映射时的上下文对象](#映射时的上下文对象)
  11. - [ObjectMapper + Alamofire](#objectmapper--alamofire)
  12. - [ObjectMapper + Realm](#objectmapper--realm)
  13. - [待完成](#待完成)
  14. - [安装](#安装)
  15. # 特性:
  16. - 把 JSON 映射成对象
  17. - 把对象映射 JSON
  18. - 支持嵌套对象 (单独的成员变量、在数组或字典中都可以)
  19. - 在转换过程支持自定义规则
  20. - 支持结构体( Struct )
  21. - [Immutable support](#immutablemappable-protocol-beta) (目前还在 beta )
  22. # 基础使用方法
  23. 为了支持映射,类或者结构体只需要实现```Mappable```协议。这个协议包含以下方法:
  24. ```swift
  25. init?(map: Map)
  26. mutating func mapping(map: Map)
  27. ```
  28. ObjectMapper使用自定义的```<-``` 运算符来声明成员变量和 JSON 的映射关系
  29. ```swift
  30. class User: Mappable {
  31. var username: String?
  32. var age: Int?
  33. var weight: Double!
  34. var array: [AnyObject]?
  35. var dictionary: [String : AnyObject] = [:]
  36. var bestFriend: User? // 嵌套的 User 对象
  37. var friends: [User]? // Users 的数组
  38. var birthday: NSDate?
  39. required init?(map: Map) {
  40. }
  41. // Mappable
  42. func mapping(map: Map) {
  43. username <- map["username"]
  44. age <- map["age"]
  45. weight <- map["weight"]
  46. array <- map["arr"]
  47. dictionary <- map["dict"]
  48. bestFriend <- map["best_friend"]
  49. friends <- map["friends"]
  50. birthday <- (map["birthday"], DateTransform())
  51. }
  52. }
  53. struct Temperature: Mappable {
  54. var celsius: Double?
  55. var fahrenheit: Double?
  56. init?(map: Map) {
  57. }
  58. mutating func mapping(map: Map) {
  59. celsius <- map["celsius"]
  60. fahrenheit <- map["fahrenheit"]
  61. }
  62. }
  63. ```
  64. 一旦你的对象实现了 `Mappable`, ObjectMapper就可以让你轻松的实现和 JSON 之间的转换。
  65. 把 JSON 字符串转成 model 对象:
  66. ```swift
  67. let user = User(JSONString: JSONString)
  68. ```
  69. 把一个 model 转成 JSON 字符串:
  70. ```swift
  71. let JSONString = user.toJSONString(prettyPrint: true)
  72. ```
  73. 也可以使用`Mapper.swift`类来完成转换(这个类还额外提供了一些函数来处理一些特殊的情况:
  74. ```swift
  75. // 把 JSON 字符串转成 Model
  76. let user = Mapper<User>().map(JSONString: JSONString)
  77. // 根据 Model 生成 JSON 字符串
  78. let JSONString = Mapper().toJSONString(user, prettyPrint: true)
  79. ```
  80. ObjectMapper支持以下的类型映射到对象中:
  81. - `Int`
  82. - `Bool`
  83. - `Double`
  84. - `Float`
  85. - `String`
  86. - `RawRepresentable` (枚举)
  87. - `Array<AnyObject>`
  88. - `Dictionary<String, AnyObject>`
  89. - `Object<T: Mappable>`
  90. - `Array<T: Mappable>`
  91. - `Array<Array<T: Mappable>>`
  92. - `Set<T: Mappable>`
  93. - `Dictionary<String, T: Mappable>`
  94. - `Dictionary<String, Array<T: Mappable>>`
  95. - 以上所有的 Optional 类型
  96. - 以上所有的隐式强制解包类型(Implicitly Unwrapped Optional)
  97. ## `Mappable` 协议
  98. #### `mutating func mapping(map: Map)`
  99. 所有的映射最后都会调用到这个函数。当解析 JSON 时,这个函数会在对象创建成功后被执行。当生成 JSON 时就只有这个函数会被对象调用。
  100. #### `init?(map: Map)`
  101. 这个可失败的初始化函数是 ObjectMapper 创建对象的时候使用的。开发者可以通过这个函数在映射前校验 JSON 。如果在这个方法里返回 nil 就不会执行 `mapping` 函数。可以通过传入的保存着 JSON 的 `Map` 对象进行校验:
  102. ```swift
  103. required init?(map: Map){
  104. // 检查 JSON 里是否有一定要有的 "name" 属性
  105. if map.JSONDictionary["name"] == nil {
  106. return nil
  107. }
  108. }
  109. ```
  110. ## `StaticMappable` 协议
  111. `StaticMappable``Mappable` 之外的另一种选择。 这个协议可以让开发者通过一个静态函数初始化对象而不是通过 `init?(map: Map)`
  112. 注意: `StaticMappable``Mappable` 都继承了 `BaseMappable` 协议。 `BaseMappable` 协议声明了 `mapping(map: Map)` 函数。
  113. #### `static func objectForMapping(map: Map) -> BaseMappable?`
  114. ObjectMapper 使用这个函数获取对象后进行映射。开发者需要在这个函数里返回一个实现 `BaseMappable` 对象的实例。这个函数也可以用于:
  115. - 在对象进行映射前校验 JSON
  116. - 提供一个缓存过的对象用于映射
  117. - 返回另外一种类型的对象(当然是必须实现了 BaseMappable)用于映射。比如你可能通过检查 JSON 推断出用于映射的对象 ([看这个例子](https://github.com/Hearst-DD/ObjectMapper/blob/master/ObjectMapperTests/ClassClusterTests.swift#L62))。
  118. 如果你需要在 extension 里实现 ObjectMapper,你需要选择这个协议而不是 `Mappable`
  119. ## `ImmutableMappable` Protocol (Beta)
  120. > ⚠️ 这个特性还处于 Beta 阶段。正式发布时 API 可能会完全不同。
  121. 使用 `ImmutableMappable` 可以映射不可变的属性。下面的表格展示了 `ImmutableMappable``Mappable` 的不同:
  122. <table>
  123. <tr>
  124. <th>ImmutableMappable</th>
  125. <th>Mappable</th>
  126. </tr>
  127. <tr>
  128. <th colspan="2">Properties</th>
  129. </tr>
  130. <tr>
  131. <td>
  132. <pre>
  133. <strong>let</strong> id: Int
  134. <strong>let</strong> name: String?
  135. </pre>
  136. </td>
  137. <td>
  138. <pre>
  139. var id: Int!
  140. var name: String?
  141. </pre>
  142. </td>
  143. </tr>
  144. <tr>
  145. <th colspan="2">JSON -> Model</th>
  146. </tr>
  147. <tr>
  148. <td>
  149. <pre>
  150. init(map: Map) <strong>throws</strong> {
  151. id = <strong>try</strong> map.value("id")
  152. name = <strong>try?</strong> map.value("name")
  153. }
  154. </pre>
  155. </td>
  156. <td>
  157. <pre>
  158. mutating func mapping(map: Map) {
  159. id <- map["id"]
  160. name <- map["name"]
  161. }
  162. </pre>
  163. </td>
  164. </tr>
  165. <tr>
  166. <th colspan="2">Model -> JSON</th>
  167. </tr>
  168. <tr>
  169. <td>
  170. <pre>
  171. mutating func mapping(map: Map) {
  172. id <strong>>>></strong> map["id"]
  173. name <strong>>>></strong> map["name"]
  174. }
  175. </pre>
  176. </td>
  177. <td>
  178. <pre>
  179. mutating func mapping(map: Map) {
  180. id <- map["id"]
  181. name <- map["name"]
  182. }
  183. </pre>
  184. </td>
  185. </tr>
  186. <tr>
  187. <th colspan="2">Initializing</th>
  188. </tr>
  189. <tr>
  190. <td>
  191. <pre>
  192. <strong>try</strong> User(JSONString: JSONString)
  193. </pre>
  194. </td>
  195. <td>
  196. <pre>
  197. User(JSONString: JSONString)
  198. </pre>
  199. </td>
  200. </tr>
  201. </table>
  202. #### `init(map: Map) throws`
  203. 这个可能抛出异常的初始化函数用于在提供的 `Map` 里映射不可变属性。每个不可变的初始化属性都要在这个初始化函数里初始化。
  204. 当发生下列情况时初始化函数会抛出一个错误:
  205. - `Map` 根据提供的键名获取不到对应值
  206. - `Map` 使用 `Transform` 后没有得到值
  207. `ImmutableMappable` 使用 `Map.value(_:using:)` 方法从 `Map` 中获取值。因为可能抛出异常,这个方法在使用时需要使用 `try` 关键字。 `Optional` 的属性可以简单的用 `try?` 处理。
  208. ```swift
  209. init(map: Map) throws {
  210. name = try map.value("name") // throws an error when it fails
  211. createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails
  212. updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional
  213. posts = (try? map.value("posts")) ?? [] // optional + default value
  214. }
  215. ```
  216. #### `mutating func mapping(map: Map)`
  217. 这个方法是在 Model 转回 JSON 时调用的。因为不可变的属性不能被 `<-` 映射,所以映射回来时需要使用 `>>>`
  218. ```swift
  219. mutating func mapping(map: Map) {
  220. name >>> map["name"]
  221. createdAt >>> (map["createdAt"], DateTransform())
  222. updatedAt >>> (map["updatedAt"], DateTransform())
  223. posts >>> map["posts"]
  224. }
  225. ```
  226. # 轻松映射嵌套对象
  227. ObjectMapper 支持使用点语法来轻松实现嵌套对象的映射。比如有如下的 JSON 字符串:
  228. ```json
  229. "distance" : {
  230. "text" : "102 ft",
  231. "value" : 31
  232. }
  233. ```
  234. 你可以通过这种写法直接访问到嵌套对象:
  235. ```swift
  236. func mapping(map: Map) {
  237. distance <- map["distance.value"]
  238. }
  239. ```
  240. 嵌套的键名也支持访问数组中的值。如果有一个返回的 JSON 是一个包含 distance 的数组,可以通过这种写法访问:
  241. ```
  242. distance <- map["distances.0.value"]
  243. ```
  244. 如果你的键名刚好含有 `.` 符号,你需要特别声明关闭上面提到的获取嵌套对象功能:
  245. ```swift
  246. func mapping(map: Map) {
  247. identifier <- map["app.identifier", nested: false]
  248. }
  249. ```
  250. 如果刚好有嵌套的对象的键名还有 `.` ,可以在中间加入一个自定义的分割符([#629](https://github.com/Hearst-DD/ObjectMapper/pull/629)):
  251. ```swift
  252. func mapping(map: Map) {
  253. appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"]
  254. }
  255. ```
  256. 这种情况的 JSON 是这样的:
  257. ```json
  258. "com.myapp.info" : {
  259. "com.myapp.name" : "SwiftOldDriver"
  260. }
  261. ```
  262. # 自定义转换规则
  263. ObjectMapper 也支持在映射时自定义转换规则。如果要使用自定义转换,创建一个 tuple(元祖)包含 ```map["field_name"]``` 和你要使用的变换放在 ```<-``` 的右边
  264. ```swift
  265. birthday <- (map["birthday"], DateTransform())
  266. ```
  267. 当解析 JSON 时上面的转换会把 JSON 里面的 Int 值转成一个 NSDate ,如果是对象转为 JSON 时,则会把 NSDate 对象转成 Int 值。
  268. 只要实现```TransformType``` 协议就可以轻松的创建自定义的转换规则:
  269. ```swift
  270. public protocol TransformType {
  271. associatedtype Object
  272. associatedtype JSON
  273. func transformFromJSON(_ value: Any?) -> Object?
  274. func transformToJSON(_ value: Object?) -> JSON?
  275. }
  276. ```
  277. ### TransformOf
  278. 大多数情况下你都可以使用框架提供的转换类 ```TransformOf``` 来快速的实现一个期望的转换。 ```TransformOf``` 的初始化需要两个类型和两个闭包。两个类型声明了转换的目标类型和源类型,闭包则实现具体转换逻辑。
  279. 举个例子,如果你想要把一个 JSON 字符串转成 Int ,你可以像这样使用 ```TransformOf``` :
  280. ```swift
  281. let transform = TransformOf<Int, String>(fromJSON: { (value: String?) -> Int? in
  282. // 把值从 String? 转成 Int?
  283. return Int(value!)
  284. }, toJSON: { (value: Int?) -> String? in
  285. // 把值从 Int? 转成 String?
  286. if let value = value {
  287. return String(value)
  288. }
  289. return nil
  290. })
  291. id <- (map["id"], transform)
  292. ```
  293. 这是一种更省略的写法:
  294. ```swift
  295. id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
  296. ```
  297. # 继承
  298. 实现了 ```Mappable``` 协议的类可以容易的被继承。当继承一个 mappable 的类时,使用这样的结构:
  299. ```swift
  300. class Base: Mappable {
  301. var base: String?
  302. required init?(map: Map) {
  303. }
  304. func mapping(map: Map) {
  305. base <- map["base"]
  306. }
  307. }
  308. class Subclass: Base {
  309. var sub: String?
  310. required init?(map: Map) {
  311. super.init(map)
  312. }
  313. override func mapping(map: Map) {
  314. super.mapping(map)
  315. sub <- map["sub"]
  316. }
  317. }
  318. ```
  319. 注意确认子类中的实现调用了父类中正确的初始化器和映射函数。
  320. # 泛型对象
  321. ObjectMapper 可以处理泛型只要这个泛型也实现了`Mappable`协议。看这个例子:
  322. ```swift
  323. class Result<T: Mappable>: Mappable {
  324. var result: T?
  325. required init?(map: Map){
  326. }
  327. func mapping(map: Map) {
  328. result <- map["result"]
  329. }
  330. }
  331. let result = Mapper<Result<User>>().map(JSON)
  332. ```
  333. # 映射时的上下文对象
  334. `Map` 是在映射时传入的对象,带有一个 optional `MapContext` 对象,开发者可以通过使用这个对象在映射时传入一些信息。
  335. 为了使用这个特性,需要先创建一个对象实现了 `MapContext` 协议(这个协议是空的),然后在初始化时传入 `Mapper` 中。
  336. ```swift
  337. struct Context: MapContext {
  338. var importantMappingInfo = "映射时需要知道的额外信息"
  339. }
  340. class User: Mappable {
  341. var name: String?
  342. required init?(map: Map){
  343. }
  344. func mapping(map: Map){
  345. if let context = map.context as? Context {
  346. // 获取到额外的信息
  347. }
  348. }
  349. }
  350. let context = Context()
  351. let user = Mapper<User>(context: context).map(JSONString)
  352. ```
  353. # ObjectMapper + Alamofire
  354. 如果网络层你使用的是 [Alamofire](https://github.com/Alamofire/Alamofire) ,并且你希望把返回的结果转换成 Swift 对象,你可以使用 [AlamofireObjectMapper](https://github.com/tristanhimmelman/AlamofireObjectMapper) 。这是一个使用 ObjectMapper 实现的把返回的 JSON 自动转成 Swift 对象的 Alamofire 的扩展。
  355. # ObjectMapper + Realm
  356. ObjectMapper 可以和 Realm 一起配合使用。使用下面的声明结构就可以使用 ObjectMapper 生成 Realm 对象:
  357. ```swift
  358. class Model: Object, Mappable {
  359. dynamic var name = ""
  360. required convenience init?(map: Map) {
  361. self.init()
  362. }
  363. func mapping(map: Map) {
  364. name <- map["name"]
  365. }
  366. }
  367. ```
  368. 如果你想要序列化相关联的 RealmObject,你可以使用 [ObjectMapper+Realm](https://github.com/jakenberg/ObjectMapper-Realm)。这是一个简单的 Realm 扩展,用于把任意的 JSON 序列化成 Realm 的类(ealm's List class。)
  369. 注意:使用 ObjectMappers 的 `toJSON` 函数来生成 JSON 字符串只在 Realm 的写事务中有效(write transaction)。这是因为 ObjectMapper 在解析和生成时在映射函数( `<-` )中使用 `inout` 作为标记( flag )。Realm 会检测到标记并且强制要求 `toJSON` 函数只能在一个写的事务中调用,即使这个对象并没有被修改。
  370. # 待完成
  371. - 改善错误的处理。可能使用 `throws` 来处理。
  372. - 相关类的文档完善
  373. # 安装
  374. ### Cocoapods
  375. 如果你的项目使用 [CocoaPods 0.36 及以上](http://blog.cocoapods.org/Pod-Authors-Guide-to-CocoaPods-Frameworks/) 的版本,你可以把下面内容添加到在 `Podfile` 中,将 ObjectMapper 添加到你的项目中:
  376. ```ruby
  377. pod 'ObjectMapper', '~> 2.2'
  378. ```
  379. ### Carthage
  380. 如果你的项目使用 [Carthage](https://github.com/Carthage/Carthage) ,你可以把下面的内容添加到 `Cartfile` 中,将 ObjectMapper 的依赖到你的项目中:
  381. ```
  382. github "Hearst-DD/ObjectMapper" ~> 2.2
  383. ```
  384. ### Swift Package Manager
  385. 如果你的项目使用 [Swift Package Manager](https://swift.org/package-manager/) ,那么你可以把下面内容添加到 `Package.swift` 中的 `dependencies` 数组中,将 ObjectMapper 的依赖到你的项目中:
  386. ```swift
  387. .Package(url: "https://github.com/Hearst-DD/ObjectMapper.git", majorVersion: 2, minor: 2),
  388. ```
  389. ### Submodule
  390. 此外,ObjectMapper 也可以作为一个 submodule 添加到项目中:
  391. 1. 打开终端,使用 `cd` 命令进入项目文件的根目录下,然后在终端中输入 `git submodule add https://github.com/Hearst-DD/ObjectMapper.git` ,把 ObjectMapper 作为项目的一个 [submodule](http://git-scm.com/docs/git-submodule) 添加进来。
  392. 2. 打开 `ObjectMapper` 文件,并将 `ObjectMapper.xcodeproj` 拖进你 app 项目的文件导航中。
  393. 3. 在 Xcode 中,文件导航中点击蓝色项目图标进入到 target 配置界面,在侧边栏的 "TARGETS" 下选择主工程对应的target。
  394. 4. 确保 `ObjectMapper.framework` 的部署版本( deployment target )和主工程的部署版本保持一致。
  395. 5. 在配置界面的顶部选项栏中,打开 "Build Phases" 面板。
  396. 6. 展开 "Target Dependencies" 组,并添加 `ObjectMapper.framework`
  397. 7. 点击面板左上角的 `+` 按钮,选择 "New Copy Files Phase"。将这个阶段重命名为 "Copy Frameworks",设置 "Destination" 为 "Frameworks",最后添加 `ObjectMapper.framework`