Swift字典转模型的思路与方法

作者: 看我的大白眼 | 来源:发表于2016-11-01 13:45 被阅读3804次

    在OC的项目中,我们遇到字典转模型的时候,一般首先是的第三方框架,例如MJExtension,YYModel,一些简单的就是用KVC.但是我在学习Swift的过程中,字典转模型也想使用MJExtension,但是发现不能很好的进行字典转模型.查阅各种资料,在此记录一下Swift中字典转模型的思路和方法.

    示例JSON

    我们解析请求天气预报的API返回的JSON

    ///  加载天气信息
        func loadWeatherInfo() {
            
            let URLString = "http://apicloud.mob.com/v1/weather/query?key=10557a5d75b9c&city=%E8%81%8A%E5%9F%8E&province=%E8%81%8A%E5%9F%8E"
            
            let url = NSURL(string: URLString)
            
            NSURLSession.sharedSession().dataTaskWithURL(url!) { (data: NSData?, response: NSURLResponse?, error: NSError?) in
                
                //省略了错误判断
                let json = (try! NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers)) as! NSDictionary
                
                print(json)
                
            }.resume()
    
        }
    

    我们把返回的JSON格式化如下图所示
    <div align = center>

    JSON
    </div>

    KVC实现字典转模型

    在使用KVC实现字典转模型的过程中,出现了很多错误.一方面是因为Swift语法不熟悉,另一方面就是因为KVC使用的不熟练.在多次尝试之后才正确的解析完成

     var msg: String?
        var result: [Result]?
        var retCode: String?
        
        init(dict: [String: AnyObject]) {
            super.init()
            
            setValuesForKeysWithDictionary(dict)
        }
        
        override func setValue(value: AnyObject?, forUndefinedKey key: String) {}
        
        override func setValue(value: AnyObject?, forKey key: String) {
            
            //判断key是否是result
            if key == "result" && (value?.isKindOfClass(NSArray))! {
              
                let temp = value as! [AnyObject]
                var resultArray = [Result]()
                for dict in temp {
                   resultArray.append(Result(dict: dict as! [String : AnyObject]))
                }
                result = resultArray
                return
            }
            
            ///  !很重要
            super.setValue(value, forKey: key)
        }
        
        override var description: String {
            let keys = ["msg", "result", "retCode"]
            return dictionaryWithValuesForKeys(keys).description
        }
    

    控制台打印模型:

    
       let model = WeatherModel(dict: json as! [String : AnyObject])
                
                print(model)
    

    控制台打印结果

    ["retCode": 200, "result": <_TtCs21_SwiftDeferredNSArray 0x7f910948dfa0>(
    ["week": 周三, "wind": 北风3级, "city": 聊城, "future": <_TtCs21_SwiftDeferredNSArray 0x7f91094aab20>(
    ["temperature": 27°C / 17°C, "wind": 北风 3~4级, "date": 2016-06-15],
    ["temperature": 33°C / 21°C, "wind": 北风 小于3级, "date": 2016-06-16],
    ["temperature": 34°C / 24°C, "wind": 南风 3~4级, "date": 2016-06-17],
    ["temperature": 34°C / 23°C, "wind": 南风 3~4级, "date": 2016-06-18],
    ["temperature": 33°C / 20°C, "wind": 南风 小于3级, "date": 2016-06-19],
    ["temperature": 29°C / 20°C, "wind": 南风 小于3级, "date": 2016-06-20],
    ["temperature": 32°C / 22°C, "wind": 西南风 2级, "date": 2016-06-21],
    ["temperature": 35°C / 22°C, "wind": 东风 2级, "date": 2016-06-22],
    ["temperature": 34°C / 22°C, "wind": 东南偏南风 3级, "date": 2016-06-23],
    ["temperature": 31°C / 21°C, "wind": 东南偏南风 3级, "date": 2016-06-24]
    )
    , "weather": 多云, "date": 2016-06-15]
    )
    , "msg": success]
    

    模型嵌套层数过多时,不建议使用KVC来实现字典转模型

    MJExtension实现字典转模型

    因为SwiftOC的数据类型很多是不统一的,所以目前OC版本的MJExtensionSwift中应该还是存在一些小问题的,比如对模型类型NSString类型的解析. CoderMJLee打算后期出一个纯Swift版本,截止现在Swift版本还没有出,大概因为是Swift还不是很成熟.但是我们依然可以在Swift中使用MJExtension.其实也就是SwiftOC实现混编

    思路:

    • 使用OC写模型类,引入bridge header然后在Swift中使用,这样用MJExtension解析就没有任何问题
    • 在swift中导入MJExtension和OC的weather类

    WearherModelOC.h

    #import <Foundation/Foundation.h>
    
    @interface WearherModelOC : NSObject
    
    /** 成功/失败 */
    @property (nonatomic, copy) NSString *msg;
    /** 返回的天气数组 */
    @property (nonatomic, strong)  NSArray *result;
    /** 请求的状态码 */
    @property (nonatomic, copy) NSString *retCode;
    
    @end
    
    @interface XNResult : NSObject
    
    /** 污染状况 */
    @property (nonatomic, copy) NSString *airCondition;
    
    /** 未来几天的天气(包括查询当天的) */
    @property (nonatomic, strong) NSArray *future;
    /** 天气 */
    @property (nonatomic, copy) NSString *weather;
    
    @end
    
    @interface XNFuture : NSObject
    
    /** 日期 */
    @property (nonatomic, strong) NSString *date;
    /** dayTime */
    @property (nonatomic, strong) NSString *dayTime;
    /** night */
    @property (nonatomic, strong) NSString *night;
    /** 当前温度 */
    @property (nonatomic, copy) NSString *temperature;
    /** week */
    @property (nonatomic, strong) NSString *week;
    /** 风向 */
    @property (nonatomic, copy) NSString *wind;
    
    
    @end
    
    

    WearherModelOC.m

    #import "WearherModelOC.h"
    
    @implementation WearherModelOC
    
    + (NSDictionary *)mj_objectClassInArray
    {
        return @{
                 @"result" : @"XNResult"
                 };
    }
    @end
    
    @implementation XNResult
    + (NSDictionary *)mj_objectClassInArray
    {
        return @{
                 @"future" : @"XNFuture"
                 };
    }
    @end
    
    @implementation XNFuture
    
    @end
    
    

    xxx-Bridging-Header.h

    #import "MJExtension.h"
    #import "WearherModelOC.h"
    
    
    • Swift中我们就可以使用MJExtension
     let model = WearherModelOC.mj_objectWithKeyValues(json)
                let result = model.result[0] as! XNResult
                print(result.airCondition)
    
    

    反射(Reflection)的介绍与使用样例

    所谓反射就是可以动态获取类型、成员信息,同时在运行时(而非编译时)可以动态调用任意方法、属性等行为的特性。

    Swift的反射机制是基于一个叫Mirrorstruct来实现的,其内部有如下属性和方法:

    let children: Children   //对象的子节点。
    displayStyle: Mirror.DisplayStyle?   //对象的展示风格
    let subjectType: Any.Type   //对象的类型
    func superclassMirror() -> Mirror?   //对象父类的 
    

    Swift反射的使用样例

    • 首先定义一个用户类:
    /// 用户类
    class User {
        
        var name: String?
        var nickName: String?
        var age: Int?
        var email: String?
        
    }
    
    
    • 创建一个用户对象,并通过反射获取这个对象的信息
    /// 创建一个User实例对象
    let user1 = User()
    user1.name = "xuning"
    user1.nickName = "hsu"
    user1.age = 25
    user1.email = "xuninghsu@gmail.com"
    ///  将user对象进行反射
    let aMirror = Mirror(reflecting: user1)
    print("对象类型: \(aMirror.subjectType)")
    print("对象子元素的个数: \(aMirror.children.count)")
    print("对象的展示风格: \(aMirror.displayStyle)")
    
    if let b = AnyBidirectionalCollection(aMirror.children) {
        
        for i in b.endIndex.advancedBy(-aMirror.children.count, limit: b.startIndex)..<b.endIndex {
        
            print(b[i])
        }
        
    }
    
    
    • 控制台打印
    对象类型: User
    对象子元素的个数: 4
    对象的展示风格: Optional(Swift.Mirror.DisplayStyle.Class)
    --- 对象子元素的属性名和属性值分别如下 ---
    (Optional("name"), Optional("xuning"))
    (Optional("nickName"), Optional("hsu"))
    (Optional("age"), Optional(25))
    (Optional("email"), Optional("xuninghsu@gmail.com"))
    
    

    我们简单了解了Mirror,然后我们再来了解一下如何使用Reflect实现字典转模型

    一键字典转模型

    直接拖拽Reflect文件夹到项目中即可,无任何第三方依赖!但是中间有一些坑.

    • 项目名称不能使用中文
    • 项目名称中不能出现横线

    我的项目名称中有横线,总是崩溃,如下图所示:

    <div align = center>


    error

    </div>

    //Model
    import Foundation
    
    class ReflectModel: Reflect {
    
        var msg: String?
        var result: [ReflectResult]?
        var retCode: String?
        
    }
    
    
    class ReflectResult: Reflect {
        
        /// 城市
        var city: String?
        /// 日期
        var date: String?
        /// 未来天气状况
        var future: [ReflectFuture]?
        /// 天气状况
        var weather: String?
        /// 周
        var week: String?
        /// 风向
        var wind: String?
    
        
    }
    
    class ReflectFuture: Reflect {
        
        /// 日期
        var date: String?
        /// 温度
        var temperature: String?
        /// 风向
        var wind: String?
        
    
    }
    
    

    使用

     let model = ReflectResult.parse(dict: json)
                
                print(model)
    
    

    参考资料

    Swift 反射 API 及用法

    Swift - 反射(Reflection)的介绍与使用样例(附KVC介绍)

    在Swift项目中保持OC环境来使用MJExtension

    Swift版的MJExtension,运行时、反射与一键字典模型互转:MJExtension-Swift

    相关文章

      网友评论

      本文标题:Swift字典转模型的思路与方法

      本文链接:https://www.haomeiwen.com/subject/egsmuttx.html