HandyJSON淺析(二)

咱們?cè)贖andyJSON淺析(一)中講了HandyJSON是怎么從類信息中獲取這個(gè)類中的屬性個(gè)數(shù)、以及屬性信息,比如屬性在實(shí)例中的偏移量,類型信息等,有了這些信息之后,剩下的就是如何將服務(wù)端返回的信息,寫到這個(gè)實(shí)例中去
  1. 獲取到的屬性信息最后都存放在properties里(此段代碼可以在HandyJSON源碼里搜到)
guard let properties = getProperties(forType: Self.self) else {
   InternalLogger.logDebug("Failed when try to get properties from type: \(type(of: Self.self))")
   return
 }
  1. properties存儲(chǔ)的信息就是所有屬性的信息,如下:包含屬性名稱、屬性類型、屬性的偏移量(key就是屬性的name)


    image.png
  2. 從服務(wù)端返回的json中解析出每個(gè)屬性對(duì)應(yīng)的值

  • 先從json中拿到屬性對(duì)應(yīng)的值
if let rawValue = getRawValueFrom(dict: _dict, property: propertyDetail, mapper: mapper)
  • 咱們?cè)倏匆幌逻@個(gè)函數(shù)的實(shí)現(xiàn)(如下),實(shí)際上大部分場景只會(huì)走最后一行,函數(shù)前面是進(jìn)行一些特殊處理的邏輯,比如服務(wù)端返回的是“id”但是我本地寫的是“carId”,咱們先講解一些正常流程的,特殊處理的后面會(huì)再單獨(dú)說
fileprivate func getRawValueFrom(dict: [String: Any], property: PropertyInfo, mapper: HelpingMapper) -> Any? {
    let address = Int(bitPattern: property.address)
    if let mappingHandler = mapper.getMappingHandler(key: address) {
        if let mappingPaths = mappingHandler.mappingPaths, mappingPaths.count > 0 {
            for mappingPath in mappingPaths {
                if let _value = dict.findValueBy(path: mappingPath) {
                    return _value
                }
            }
            return nil
        }
    }
    if HandyJSONConfiguration.deserializeOptions.contains(.caseInsensitive) {
        return dict[property.key.lowercased()]
    }
    //大部分場景只會(huì)走這一行,很簡單就是根據(jù)key從json里取出對(duì)應(yīng)的數(shù)據(jù)
    return dict[property.key]
}
  • 對(duì)取出的數(shù)據(jù)后,再看看符不符合要求,比如類型是否對(duì)應(yīng)的上,如果對(duì)應(yīng)不上,再進(jìn)行簡單的類型轉(zhuǎn)換處理
 if let convertedValue = convertValue(rawValue: rawValue, property: propertyDetail, mapper: mapper)

函數(shù)實(shí)現(xiàn)如下

fileprivate func convertValue(rawValue: Any, property: PropertyInfo, mapper: HelpingMapper) -> Any? {
   //不對(duì)數(shù)據(jù)進(jìn)行特殊處理的話,這是不會(huì)執(zhí)行的
    if rawValue is NSNull { return nil }
    if let mappingHandler = mapper.getMappingHandler(key: Int(bitPattern: property.address)), let transformer = mappingHandler.assignmentClosure {
        return transformer(rawValue)
    }
   
   //大部分情況會(huì)走這
    if let transformableType = property.type as? _Transformable.Type {//如果已經(jīng)兼容這種屬性類型的處理。還需要注意一點(diǎn)的就是,如果寫成可選型,會(huì)多一層轉(zhuǎn)換,先將可選類型解包成對(duì)應(yīng)類型,再進(jìn)行轉(zhuǎn)換,所以如果可以的話,盡量少的用可選類型
        return transformableType.transform(from: rawValue)//如果需要的話,會(huì)進(jìn)行簡單的類型處理,比如我定義的是bool,結(jié)果服務(wù)端返回了個(gè)字符串"0",那就會(huì)自動(dòng)處理為false在第一篇文章里講了。
    } else {//如果這個(gè)庫沒兼容的類型,則直接獲取
        return extensions(of: property.type).takeValue(from: rawValue)
    }
}
//舉個(gè)例子:自己定義的類型和服務(wù)端返回的不一致時(shí)的處理,還有別的類型處理,可以去庫里看
extension Bool: _BuiltInBasicType {

    static func _transform(from object: Any) -> Bool? {
        switch object {
        case let str as NSString:
            let lowerCase = str.lowercased
            if ["0", "false"].contains(lowerCase) {
                return false
            }
            if ["1", "true"].contains(lowerCase) {
                return true
            }
            return nil
        case let num as NSNumber:
            return num.boolValue
        default:
            return nil
        }
    }

    func _plainValue() -> Any? {
        return self
    }
}
  1. 想要的數(shù)據(jù)拿到以后,就開始賦值了,函數(shù)如下
assignProperty(convertedValue: convertedValue, instance: instance, property: propertyDetail)

函數(shù)實(shí)現(xiàn)如下

fileprivate func assignProperty(convertedValue: Any, instance: _ExtendCustomModelType, property: PropertyInfo) {
    if property.bridged {
        //如果是繼承自NsObject的類型,也就是OC類型,那可以直接用KVC進(jìn)行賦值
        (instance as! NSObject).setValue(convertedValue, forKey: property.key)
    } else {
        //如果是Swift類型,那就往實(shí)例中對(duì)應(yīng)屬性的位置里寫上服務(wù)端返回的值
        extensions(of: property.type).write(convertedValue, to: property.address)
    }
}



//extensions(of: property.type).write這個(gè)方法點(diǎn)進(jìn)去就會(huì)發(fā)現(xiàn),實(shí)際上最重要的是這個(gè)方法
public static func write(_ value: Any, to storage: UnsafeMutableRawPointer) {
        guard let this = value as? Self else {
            return
        }
        storage.assumingMemoryBound(to: self).pointee = this
    }
  1. 特殊情況的處理
    一些特殊需求的處理,官方也給出了一些Demo。
    地址是https://github.com/alibaba/HandyJSON/blob/master/README_cn.md,咱們挑幾個(gè)說一下
  • 指定解析路徑
HandyJSON支持指定從哪個(gè)具體路徑開始解析,反序列化到Model。

class Cat: HandyJSON {
    var id: Int64!
    var name: String!

    required init() {}
}

let jsonString = "{\"code\":200,\"msg\":\"success\",\"data\":{\"cat\":{\"id\":12345,\"name\":\"Kitty\"}}}"

if let cat = Cat.deserialize(from: jsonString, designatedPath: "data.cat") {
    print(cat.name)
}

實(shí)現(xiàn)原理
public static func deserializeFrom(dict: [String: Any]?, designatedPath: String? = nil) -> T? {
     var targetDict = dict
     if let path = designatedPath {//如果指定了解析節(jié)點(diǎn),則獲取指定節(jié)點(diǎn)后的數(shù)據(jù)
          targetDict = getInnerObject(inside: targetDict, by: path) as? [String: Any]
      }
     if let _dict = targetDict {//如果沒指定解析路徑,則直接解析返回的所有數(shù)據(jù)
        return T._transform(dict: _dict) as? T
     }
     return nil
    }

//獲取某個(gè)節(jié)點(diǎn)后的數(shù)據(jù)的實(shí)現(xiàn)如下,也就是從總的json里,根據(jù)指定的節(jié)點(diǎn),一層一層的往下獲取
fileprivate func getInnerObject(inside object: Any?, by designatedPath: String?) -> Any? {
    var result: Any? = object
    var abort = false
    if let paths = designatedPath?.components(separatedBy: "."), paths.count > 0 {
        var next = object as? [String: Any]
        paths.forEach({ (seg) in
            if seg.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "" || abort {
                return
            }
            if let _next = next?[seg] {
                result = _next
                next = _next as? [String: Any]
            } else {
                abort = true
            }
        })
    }
    return abort ? nil : result
}
  • 自定義解析規(guī)則,這是官方Demo里的例子
class Cat: HandyJSON {
    var id: Int64!
    var name: String!
    var parent: (String, String)?

    required init() {}

    func mapping(mapper: HelpingMapper) {
        // specify 'cat_id' field in json map to 'id' property in object
        mapper <<<
            self.id <-- "cat_id"

        // specify 'parent' field in json parse as following to 'parent' property in object
        mapper <<<
            self.parent <-- TransformOf<(String, String), String>(fromJSON: { (rawString) -> (String, String)? in
                if let parentNames = rawString?.characters.split(separator: "/").map(String.init) {
                    return (parentNames[0], parentNames[1])
                }
                return nil
            }, toJSON: { (tuple) -> String? in
                if let _tuple = tuple {
                    return "\(_tuple.0)/\(_tuple.1)"
                }
                return nil
            })

        // specify 'friend.name' path field in json map to 'friendName' property
        mapper <<<
            self.friendName <-- "friend.name"
    }
}

let jsonString = "{\"cat_id\":12345,\"name\":\"Kitty\",\"parent\":\"Tom/Lily\",\"friend\":{\"id\":54321,\"name\":\"Lily\"}}"

if let cat = Cat.deserialize(from: jsonString) {
    print(cat.id)
    print(cat.parent)
    print(cat.friendName)
}

咱們說一下比較常見的,本地變量的名稱和服務(wù)端返回名稱不一致轉(zhuǎn)換的實(shí)現(xiàn),在以前編寫OC時(shí)經(jīng)常遇到,咱們變量名不能寫成id,但是服務(wù)端返回的是id,就需要轉(zhuǎn)換了(但是官方給的demo里,是本地名稱是id,服務(wù)端返回的是cat_id,別混了)
<<< 和 <--是自定義的運(yùn)算符,<--的優(yōu)先級(jí)更高

 mapper <<< self.id <-- "cat_id"

先說一下<--

infix operator <-- : LogicalConjunctionPrecedence

public func <-- <T>(property: inout T, name: String) -> CustomMappingKeyValueTuple {
    return property <-- [name]
}

//運(yùn)算符<--會(huì)返回一個(gè)元組,簡單點(diǎn)理解就是,元組里第一個(gè)數(shù)據(jù)是屬性id的地址,元組第二個(gè)數(shù)據(jù)是服務(wù)端返回對(duì)應(yīng)數(shù)據(jù)的真實(shí)key,以demo中例子說就類似(0x39393994949, cat_id)
public func <-- <T>(property: inout T, names: [String]) -> CustomMappingKeyValueTuple {
    let pointer = withUnsafePointer(to: &property, { return $0 })
    let key = Int(bitPattern: pointer)
    return (key, MappingPropertyHandler(rawPaths: names, assignmentClosure: nil, takeValueClosure: nil))
}

再說一下 <<<

infix operator <<< : AssignmentPrecedence

//<<<執(zhí)行這個(gè)最終就是將上面說的元組的內(nèi)容添加到了self.mappingHandlers,實(shí)際執(zhí)行的是self.mappingHandlers[key] = mappingInfo(key是元組的第一個(gè)元素,mappingInfo是元組的第二個(gè)元素)

public func <<< (mapper: HelpingMapper, mapping: CustomMappingKeyValueTuple) {
    mapper.addCustomMapping(key: mapping.0, mappingInfo: mapping.1)
}

最后說一下是怎么替換的,咱們?cè)谧铋_始的時(shí)候說過下面這個(gè)函數(shù),就是獲取對(duì)應(yīng)字段的數(shù)據(jù),說的是正常情況下,只會(huì)走最后一行,也就是直接根據(jù)屬性的name找到value,但是需要name轉(zhuǎn)換的時(shí)候,就會(huì)走前面的代碼了,會(huì)根據(jù)服務(wù)端返回的name,找到對(duì)應(yīng)的value(因?yàn)閷傩缘膎ame和服務(wù)端返回的不一致)

fileprivate func getRawValueFrom(dict: [String: Any], property: PropertyInfo, mapper: HelpingMapper) -> Any? {
    let address = Int(bitPattern: property.address)
    //因?yàn)樵蹅冎皥?zhí)行了self.mappingHandlers[key] = mappingInfo,所以執(zhí)行到這就會(huì)有值了,說白了就是,服務(wù)端返回這個(gè)字段的實(shí)際key是啥,然后根據(jù)findValueBy這個(gè)方法,用服務(wù)端返回的實(shí)際屬性名為key,找到對(duì)應(yīng)數(shù)據(jù)
    if let mappingHandler = mapper.getMappingHandler(key: address) {
        if let mappingPaths = mappingHandler.mappingPaths, mappingPaths.count > 0 {
            for mappingPath in mappingPaths {
                if let _value = dict.findValueBy(path: mappingPath) {
                    return _value
                }
            }
            return nil
        }
    }
    if HandyJSONConfiguration.deserializeOptions.contains(.caseInsensitive) {
        return dict[property.key.lowercased()]
    }
    return dict[property.key]
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • HandyJSON的基本原理就是從類信息里獲取所有屬性的特征,包括名稱,屬性在內(nèi)存里的偏移量、屬性的個(gè)數(shù)、屬性的類...
    黑色螞蟻_MGL閱讀 4,879評(píng)論 4 0
  • 在上一篇文章中, 我著重介紹了 Swift中指針的使用, 這篇文章主要圍繞以下幾點(diǎn): HandyJSON 的優(yōu)勢(shì)....
    Lin__Chuan閱讀 4,555評(píng)論 0 12
  • 處理JSON數(shù)據(jù)是在開發(fā)過程中一定會(huì)遇到的一項(xiàng)操作,通常情況下我們會(huì)先把JSON轉(zhuǎn)為Dictionary,記住每個(gè)...
    QH_hao閱讀 7,036評(píng)論 0 40
  • JSON是移動(dòng)端開發(fā)常用的應(yīng)用層數(shù)據(jù)交換協(xié)議。最常見的場景便是,客戶端向服務(wù)端發(fā)起網(wǎng)絡(luò)請(qǐng)求,服務(wù)端返回JSON文本...
    水落斜陽閱讀 3,656評(píng)論 0 15
  • HandyJSON阿里巴巴開源的Swift環(huán)境下用的Json轉(zhuǎn)模型工具。 為什么用HandyJSON 在Swift...
    JoeXP閱讀 3,284評(píng)論 3 2

友情鏈接更多精彩內(nèi)容