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

在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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,943评论 4 60
  • 7月30日 晴 一直忙碌的奔波着,今天偶尔清闲,接踵而来的意外让原本的那个把一切都规划好的姑娘扯进泥沼,越挣扎越...
    大玲玲啊阅读 161评论 0 0
  • XX是我的一个好朋友。昨晚她跟我说了一件事。她说她妈绝经了。她说,我妈跟我说这件事的时候,我真的震惊了。(请原谅我...
    思语者2016阅读 212评论 0 0
  • 也许是吸引力法则的影响,在我需要时恰巧遇见了这款工具,这个月常常用来梳理拟写文章的思路和一些工作上的事情,越用越欢...
    风起云卷纵四海阅读 703评论 2 12