上述主要了解的ObjectMapper的基本使用和核心的文件,此篇文章主要记述了ObjectMapper的各个文件的详解。
Mappable
这个文件中主要定义了可映射的协议组。
- BaseMappable:抽象协议,不能直接使用。
// 定义了mapping方法
mutating func mapping(map: Map)
- Mappable :继承BaseMappable协议。
//在映射之前使用这个方法去验证Json数据,返回nil的时候就取消映射
init?(map: Map)
- StaticMappable :继承BaseMappable协议。
//1. 提供用于映射的现有对象 2. 返回另一个符合BaseMappable协议的类对象用于映射。
static func objectForMapping(map: Map) -> BaseMappable?
- BaseMappable扩展:在BaseMappable扩展中默认实现如下协议。主要使用Mapper映射的方法实现。
// 从JSON字符串中初始化对象
public init?(JSONString: String, context: MapContext? = nil)
// 从JSON字典中初始化对象
public init?(JSON: [String: Any], context: MapContext? = nil)
// 将对象转换到JSON字典
public func toJSON() -> [String: Any]
// 将对象转换到JSON字符串
public func toJSONString(prettyPrint: Bool = false) -> String?
Array扩展:Array中元素符合BaseMappable协议,重写上述扩展协议方法。主要使用Mapper映射器的方法实现
Set扩展:Set中元素符合BaseMappable协议,重写上述扩展协议方法。主要使用Mappe映射器的方法实现
Map
这个文件中主要定义了Map类持有映射的数据。
在Map类中定义了一组下标方法:主要作用得到一个持有key和value的Map实例。
// 这个方法的参数 key:键值,nested:是否嵌套(一般根据分隔符判断),delimiter:分割符,ignoreNil:是否忽略nil值
public subscript(key: String, nested nested: Bool, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map {
// save key and value associated to it
// 规定currentKey 当前key ,keyIsNested 键值是否嵌套, nestedKeyDelimiter 嵌套key值的分隔符
currentKey = key
keyIsNested = nested
nestedKeyDelimiter = delimiter
if mappingType == .fromJSON { // 从JSON中
// check if a value exists for the current key
// do this pre-check for performance reasons
if nested == false { // 没有嵌套
let object = JSON[key]
let isNSNull = object is NSNull
isKeyPresent = isNSNull ? true : object != nil // 当是null值,isKeyPresent是true;否则,当object不是nil的时候显示
currentValue = isNSNull ? nil : object // 当是null值,当前值是nil,否者是当是object
} else { // 嵌套中
// break down the components of the key that are separated by . 根据分隔符产生数组,从中获取元组(是否显示key,当前值)
(isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON)
}
// update isKeyPresent if ignoreNil is true
if ignoreNil && currentValue == nil { // 如果忽略nil值,同时currentValue为nil的时候,不显示key值
isKeyPresent = false
}
}
return self // toJSON类型直接返回self
}
两个valueFor
私有方法:分别针对字典类型和数组类型,解嵌套。两者之间会有递归和相互调用。
private func valueFor(_ keyPathComponents: ArraySlice<String>, dictionary: [String: Any]) -> (Bool, Any?) {
// Implement it as a tail recursive function.
if keyPathComponents.isEmpty { // 为空
return (false, nil)
}
if let keyPath = keyPathComponents.first { // keyPathComponents = [bestFriend,username]
let object = dictionary[keyPath]
if object is NSNull { // 如果object是NSNull类型,返回(true, nil)
return (true, nil)
} else if keyPathComponents.count > 1, let dict = object as? [String: Any] { // 如果object是字典型,同时keyPathComponents大于1
let tail = keyPathComponents.dropFirst() // 放弃第一个key值
return valueFor(tail, dictionary: dict) // 再次调用自己
} else if keyPathComponents.count > 1, let array = object as? [Any] { // 如果object是数组型,同时keyPathComponents大于1
let tail = keyPathComponents.dropFirst() // 放弃第一个key值
return valueFor(tail, array: array) // 调用array的方法
} else {
return (object != nil, object) // 返回此类型
}
}
return (false, nil) // 默认返回这个数据
}
Mapper
这个文件定义了泛型类型Mapper<N: BaseMappable>
,这个类是作为映射器使用,提供了数据和模型之间的转换过程方法。
单一值类型
map方法的参数分别为JSONObject,JSONString和JSON三种类型。
1. 首先对于JSONString的map方法,是有Mapper类的解析方法parseJSONStringIntoDictionary
将字符串解析至字典。
2. 对于JSONObject的map方法,使用as?
关键字将对象转换至字典。
3. 最后调用的方法都是JSON字典的map方法。
指定的对象map方法:
public func map(JSON: [String: Any], toObject object: N) -> N {
var mutableObject = object
// 创建map对象,调用mapping方法。返对象
let map = Map(mappingType: .fromJSON, JSON: JSON, toObject: true, context: context, shouldIncludeNilValues: shouldIncludeNilValues)
mutableObject.mapping(map: map)
return mutableObject
}
非指定对象map方法:
public func map(JSON: [String: Any]) -> N? {
// 创建一个Map类,从JSON中获取
let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues)
// 获取N的类型,并匹配相应的类型
if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable
// 如果是StaticMappable静态映射实例,从map中解除一个BaseMapple实例,转换成N类型的值
if var object = klass.objectForMapping(map: map) as? N {
object.mapping(map: map) // 实现BaseMappablede的mapping方法
return object // 返回object值
}
} else if let klass = N.self as? Mappable.Type { // Check if object is Mappable
// 如果是Mappable实例,实现Mappable的初始化协议并转换成N的类型
if var object = klass.init(map: map) as? N {
object.mapping(map: map) // 实现BaseMappablede的mapping方法
return object // 返回object值
}
} else if let klass = N.self as? ImmutableMappable.Type { // Check if object is ImmutableMappable
// 如果是不可变的Mappable
do {
return try klass.init(map: map) as? N // 转换成功返回object值
} catch let error {
#if DEBUG
let exception: NSException
// 转换成MapError类型
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()
#else
NSLog("\(error)")
#endif
}
} else {
// Ensure BaseMappable is not implemented directly 确保BaseMappale不能直接别实现
assert(false, "BaseMappable should not be implemented directly. Please implement Mappable, StaticMappable or ImmutableMappable")
}
return nil // 默认返回nil
}
集合值类型
同单一值类型相同,我们的参数分别为JSONObject,JSONString和JSON三种类型。而我们最终调取JSON字典类型map方法进行操作。
mapArray : 一维数组的对象集合,flatmap方法中调用的map方法是func map(JSON: [String: Any]) -> N?
方法。即将数组中的每一个字典进行映射。
public func mapArray(JSONArray: [[String: Any]]) -> [N] {
// map every element in JSON array to type N
let result = JSONArray.flatMap(map)
return result
}
mapDictionary : 一维字典的对象集合,filterMap方法中调用的map方法是func map(JSON: [String: Any]) -> N?
方法。即将字典中的每一个字典进行映射。
public func mapDictionary(JSON: [String: [String: Any]]) -> [String: N]? {
// map every value in dictionary to type N
// filterMap:自定义筛选方法,Dictionary扩展方法,当map成功添加到字典中,否则不添加
let result = JSON.filterMap(map)
if result.isEmpty == false { // 筛选结果不为空
return result
}
return nil
}
mapDictionary:toDictionary:指定字典的映射类型
public func mapDictionary(JSON: [String: [String: Any]], toDictionary dictionary: [String: N]) -> [String: N] {
var mutableDictionary = dictionary
for (key, value) in JSON { // [:]
if let object = dictionary[key] { // 从key值解出一个对象
_ = map(JSON: value, toObject: object) // 通过JSON和目标对象进行映射
} else {
mutableDictionary[key] = map(JSON: value) // 通过字典映射
}
}
return mutableDictionary
}
mapDictionaryOfArrays: 字典嵌套数组的集合类型,对于字典中的每一个数组进行mapArray
的方法调用
public func mapDictionaryOfArrays(JSON: [String: [[String: Any]]]) -> [String: [N]]? {
// map every value in dictionary to type N
let result = JSON.filterMap {
mapArray(JSONArray: $0) // 从JSON数组中获取[[String: Any]]中获取[N]
}
if result.isEmpty == false {
return result
}
return nil
}
mapArrayOfArrays:双重数组嵌套类型
public func mapArrayOfArrays(JSONObject: Any?) -> [[N]]? {
if let JSONArray = JSONObject as? [[[String: Any]]] { // 转换类型 [[[:]]] 三重嵌套
var objectArray = [[N]]()
for innerJSONArray in JSONArray { // innerJSONArray 是 [[:]]
//调用mapArray返回,[N]
let array = mapArray(JSONArray: innerJSONArray)
objectArray.append(array) // 添加进对象数组中 [[N]]
}
if objectArray.isEmpty == false { // 返回结果不为空
return objectArray
}
}
return nil
}
toJSON方法
toJSON主要分为转换成字典类型,字符串类型和Data数据类型。
字典核心方法:
public func toJSON(_ object: N) -> [String: Any] {
var mutableObject = object
let map = Map(mappingType: .toJSON, JSON: [:], context: context, shouldIncludeNilValues: shouldIncludeNilValues)
mutableObject.mapping(map: map) // 映射
return map.JSON
}
Data数据类型核心方法:
public static func toJSONData(_ JSONObject: Any, options: JSONSerialization.WritingOptions) -> Data? {
if JSONSerialization.isValidJSONObject(JSONObject) { // 是不是有效的对象
let JSONData: Data?
do {
JSONData = try JSONSerialization.data(withJSONObject: JSONObject, options: options) // JSON解析成数据类型
} catch let error {
print(error)
JSONData = nil // 设置是nil值
}
return JSONData
}
return nil
}
字符串类型核心方法:将对象转换成Data数据,通过编码转化字符串类型
public static func toJSONString(_ JSONObject: Any, prettyPrint: Bool) -> String? {
let options: JSONSerialization.WritingOptions = prettyPrint ? .prettyPrinted : []
if let JSON = Mapper.toJSONData(JSONObject, options: options) { // 解包
return String(data: JSON, encoding: String.Encoding.utf8) // 从数据中UTF-8编码方式转换成字符串
}
return nil
}