在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
格式化如下图所示
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实现字典转模型
因为Swift
和OC
的数据类型很多是不统一的,所以目前OC
版本的MJExtension
在Swift
中应该还是存在一些小问题的,比如对模型类型NSString
类型的解析. CoderMJLee
打算后期出一个纯Swift
版本,截止现在Swift
版本还没有出,大概因为是Swift
还不是很成熟.但是我们依然可以在Swift中使用MJExtension
.其实也就是Swift
和OC
实现混编
思路:
- 使用OC写模型类,引入
bridge header
然后在Swift
中使用,这样用MJExtension
解析就没有任何问题 - 在swift中导入MJExtension和OC的weather类
WearherModelOC.h
#import
@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
的反射机制是基于一个叫Mirror
的struct
来实现的,其内部有如下属性和方法:
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 = "[email protected]"
/// 将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)..
- 控制台打印
对象类型: User
对象子元素的个数: 4
对象的展示风格: Optional(Swift.Mirror.DisplayStyle.Class)
--- 对象子元素的属性名和属性值分别如下 ---
(Optional("name"), Optional("xuning"))
(Optional("nickName"), Optional("hsu"))
(Optional("age"), Optional(25))
(Optional("email"), Optional("[email protected]"))
我们简单了解了Mirror
,然后我们再来了解一下如何使用Reflect
实现字典转模型
一键字典转模型
直接拖拽Reflect文件夹到项目中即可,无任何第三方依赖!但是中间有一些坑.
- 项目名称不能使用中文
- 项目名称中不能出现横线
我的项目名称中有横线,总是崩溃,如下图所示:
//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