ObjectMapper 是一款基于Swfit非常有用的Json转模型的第三方框架。 ObjectMapper 和原生的Codable 来转模型的区别在我的另一篇文章Swift 转模型 Codable和ObjectMapper 使用比较 中提过,这里就不再重复。
导读: 用ObjectMapper 转模型
book.json
{
"title": "War and Peace: A protocol oriented approach to diplomacy",
"author": "A. Keed Decoder",
"rating": 5.0,
}
XXBook
模型
import ObjectMapper
struct XXBook: Mappable {
// 通过Mapper转模型
var title: String?
var author: String?
var rating: Float?
init?(map: Map) {
}
mutating func mapping(map: Map) {
title <- map["title"]
author <- map["author"]
rating <- map["rating"]
}
}
使用: 将json转成功之后直接调用
// 用ObjectMapper转模型
let book = XXBook(JSON: json)
下面来探索为什么 XXBook(JSON: json)
就可以转模型成功。这篇文章仅从Json转模型中的主要方法进行分析,会略过暂未用到的逻辑。
这里放一张协议方法图
主流程
一、首先XXBook
遵循了Mappable
协议, Mappable
又遵循了BaseMappable
协议。 Mappable
有个必须要实现的init?(map: Map)
方法,BaseMappable
必须要实现 mutating func mapping(map: Map)
方法。 所以上述的 XXBook
包含了这两个方法。
二、 json转模型时 XXBook(JSON: json)
调用的方法
调用
XXBook(JSON: json)
时其实调用了BaseMappable
协议中默认实现了的方法public init?(JSON: [String: Any], context: MapContext? = nil)
上面那个方法又会调用
Mapper(context: context).map(JSON: JSON)
这个方法顺利的话会返回一个对象,即我们需要的book对象返回对象的主要代码时
Mapper
类里面的
public func map(JSON: [String: Any]) -> N? {
let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues)
if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable
if var object = klass.objectForMapping(map: map) as? N {
object.mapping(map: map)
return object
}
} else if let klass = N.self as? Mappable.Type { // Check if object is Mappable
if var object = klass.init(map: map) as? N {
object.mapping(map: map)
return object
}
} else if let klass = N.self as? ImmutableMappable.Type { // Check if object is ImmutableMappable
do {
return try klass.init(map: map) as? N
} catch let error {
#if DEBUG
let exception: NSException
if let mapError = error as? MapError {
exception = NSException(name: .init(rawValue: "MapError"), reason: mapError.description, userInfo: nil)
} else {
exception = NSException(name: .init(rawValue: "ImmutableMappableError"), reason: error.localizedDescription, userInfo: nil)
}
exception.raise()
#endif
}
} else {
// Ensure BaseMappable is not implemented directly
assert(false, "BaseMappable should not be implemented directly. Please implement Mappable, StaticMappable or ImmutableMappable")
}
return nil
}
- XXBook类型遵守的是
Mappable
协议,所以这里的核心代码是
// 初始化一个map
let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues)
以及
else if let klass = N.self as? Mappable.Type { // Check if object is Mappable
//重要代码: 根据map初始化了一个object对象,N范型其实这里对应的就是XXBook
if var object = klass.init(map: map) as? N {
//重要代码:根据map。 对object中的属性赋值
object.mapping(map: map)
return object
}
}
- 看看
object.mapping(map: map)
干了什么
mutating func mapping(map: Map) {
title <- map["title"]
author <- map["author"]
rating <- map["rating"]
}
Operators.swift
中可以看到定义了<-
操作符, 可选类型会走下面这个方法
/// Optional object of basic type
// inout 类似于指针,这里是想改变操作符左边的值。
public func <- (left: inout T?, right: Map) {
switch right.mappingType {
case .fromJSON where right.isKeyPresent:
FromJSON.optionalBasicType(&left, object: right.value())
case .toJSON:
left >>> right
default: ()
}
}
可以看到这里的重要代码是
class func optionalBasicType(_ field: inout FieldType?, object: FieldType?) {
field = object
}
对这里就是赋值了。类似于这种赋值,object.name = map["name"]
。 当然,map["name"]
肯定还包含一些内容,毕竟值从哪儿来的还没看到。
- 上一步中初始化过一个map, 这个map包含里储存了JSON的值。 并且定义了下标函数, 即调用
map["name"]
时会走Map.swift
中的public subscript(key: String) -> Map
方法。 这个方法调用的核心方法是:
private func `subscript`(key: String, nested: Bool? = nil, delimiter: String = ".", ignoreNil: Bool = false) -> Map {
// save key and value associated to it
currentKey = key
keyIsNested = nested ?? key.contains(delimiter)
nestedKeyDelimiter = delimiter
if mappingType == .fromJSON {
// check if a value exists for the current key
// do this pre-check for performance reasons
if keyIsNested {
// break down the components of the key that are separated by delimiter
(isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON)
} else {
// 如果不包含内嵌的模型,那么获取key值的重要语句就在这里了
let object = JSON[key]
let isNSNull = object is NSNull
isKeyPresent = isNSNull ? true : object != nil
currentValue = isNSNull ? nil : object
}
// update isKeyPresent if ignoreNil is true
if ignoreNil && currentValue == nil {
isKeyPresent = false
}
}
return self
}
- 根据
map["name"]
传进来的key
, 获取到当前的currentValue
。 - 一一为每个
mapping
方法中的属性赋值,完了之后返回一个对象。 整个过程就结束了。
if var object = klass.init(map: map) as? N {
//重要代码:根据map。 对object中的属性赋值
object.mapping(map: map)
return object
}
以上就是JSON转模型的全部过程。
探索
- 上篇提到,
json
返回的rating
为String
类型或者Null
时,属性会赋值为nil
//定义的 <- 操作符调用的 right.value()
public func <- (left: inout T?, right: Map) {
switch right.mappingType {
case .fromJSON where right.isKeyPresent:
FromJSON.optionalBasicType(&left, object: right.value())
case .toJSON:
left >>> right
default: ()
}
}
right.value()
调用了Map.swift
的 value
方法, 方法中的let value = currentValue as? T
类型不对value只能是nil了
public func value() -> T? {
let value = currentValue as? T
// Swift 4.1 breaks Float casting from `NSNumber`. So Added extra checks for `Float` `[Float]` and `[String:Float]`
if value == nil && T.self == Float.self {
if let v = currentValue as? NSNumber {
return v.floatValue as? T
}
} else if value == nil && T.self == [Float].self {
if let v = currentValue as? [Double] {
#if swift(>=4.1)
return v.compactMap{ Float($0) } as? T
#else
return v.flatMap{ Float($0) } as? T
#endif
}
} else if value == nil && T.self == [String:Float].self {
if let v = currentValue as? [String:Double] {
return v.mapValues{ Float($0) } as? T
}
}
return value
}