Swift-实现字典模型转换(一)
最近太忙太久没写文章了,感觉有点不会写了。
好了,废话不多说开始swift模型转字典,字典转模型的小小工具类编写和思路。
思路:
以前 OC
是使用 runtime
获取到 Mode
l 里面的 key
- value
然后用KVC
进行赋值。
那么Swift
也可以使用 Mirror
来获取 Model
里面的 key
- value
哦~,~。
有了思路我现在就开始编写,我在项目遇到的坑还有编写遇到的坑,还有些没解决的坑。😄
以前 OC
中写Model
会继承 NSObject
,按我的习惯来,在Swift
我也习惯用NSObject
来写Model
,所以现在我就用 NSObject
来写扩展(extension
)。
第一步,使用Mirror
动态获取Model
的key
。
第二步,创建Model
,使用KVC
赋值。
第三步,然后再转Dictionary
。
基本步骤就这样子把。
模型:
class Person : NSObject {
var name:String = ""
var age:Int = 0
var desc:String?
var height:Double?
}
把大部分可能性都写了下,好测试。
有些坑都在?
上,也就是Optional(可选值)
🤦♂️。
扩展:
第一步:
先来点简单的,使用Mirror
打印Model的 key
,type
extension NSObject {
func variables() {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一个 (label: String?, value: Any) 元组类型,表示该类的所有属性的名字和类型
for case let (label,value) in mirr.children {
if let key = label {
let valueMirr = Mirror(reflecting: value)
//subjectType 类型
debugPrint("key -- \(key) type -- \(valueMirr.subjectType)")
}
}
}
}
终端打印:
Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
"key -- name type -- String"
"key -- age type -- Int"
"key -- desc type -- Optional<String>"
"key -- height type -- Optional<Double>"
然后就能看到写 key
type
,这样就好办多了。知道key
就可以进行KVC
了。
为了以后更可读一点,把一些常用基本类型进行简单的封装成枚举。
enum VariableType {
case number //数字
case string //字符串
case bool //布尔
case array(String) //数组 String 是对应对象
case dictionary //字典
case object //对象
case null //NSNull
case unknown //无法解析数据
}
这一招我是模仿SwfitlyJson
在做的,可以在Github搜索到😄。
我现在就将数据进行分类组合~~
typealias VarData = (String,VariableType,String,Any.Type)// key type modelStr class 数据结构
extension NSObject {
func variables() {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一个 (label: String?, value: Any) 元组类型,表示该类的所有属性的名字和类型
var keys = [VarData]()
keys.append(contentsOf:self.categroy(mirr))
debugPrint(keys)
}
func categroy(_ mirr: Mirror) -> [VarData] {
var keys = [VarData]()
for case let (label,value) in mirr.children {
if let key = label {
let valueMirr = Mirror(reflecting: value)
var type = VariableType.unknown
var str = ""
if valueMirr.subjectType == String?.self || valueMirr.subjectType == String.self {
type = .string
} else if valueMirr.subjectType == Int?.self ||
valueMirr.subjectType == Int.self ||
valueMirr.subjectType == Int64?.self ||
valueMirr.subjectType == Int64.self ||
valueMirr.subjectType == Float?.self ||
valueMirr.subjectType == Float.self ||
valueMirr.subjectType == Double?.self ||
valueMirr.subjectType == Double.self {
type = .number
} else if valueMirr.subjectType == Bool?.self ||
valueMirr.subjectType == Bool.self {
type = .bool
} else {
let typestr = "\(valueMirr.subjectType)"
if typestr.contains("Array") {
str = typestr
str = str.replacingOccurrences(of: "Optional<", with: "")
str = str.replacingOccurrences(of: "Array<", with: "")
str = str.replacingOccurrences(of: ">", with: "")
type = .array(str)
} else if typestr.contains("Dictionary") {
type = .dictionary
} else {
if valueMirr.subjectType is NSObject.Type {
type = .object
} else if valueMirr.subjectType is NSObject?.Type {
type = .object
}
}
}
keys.append((key,type,str,valueMirr.subjectType))
}
}
return keys
}
}
Test Case '-[ObjectConversionTests.ObjectConversionTests testModel]' started.
[("name", ObjectConversion.VariableType.string, "", Swift.String),
("age", ObjectConversion.VariableType.number, "", Swift.Int),
("desc", ObjectConversion.VariableType.string, "", Swift.Optional<Swift.String>),
("height", ObjectConversion.VariableType.number, "", Swift.Optional<Swift.Double>)]
数据改造后就能清晰的看清楚数据对应的类型是啥了,是不是瞬间感觉友善很多了哈。
你们应该会有点疑问为啥那么多基础类型都做双判断,这就是坑点。
if valueMirr.subjectType == Double.self {
...
}
起初我的判断是这样的以上判断。
但是他识别不了Optional
的变量,所以我基本数据类型都会判断Optional
。
if valueMirr.subjectType == Double.self
|| valueMirr.subjectType == Double?.self {
...
}
所以我才改成上面这样子=,=。
本来我是想过用字符串来进行判断的,后来想到这只是基本数据类型,转来转去太麻烦了。
话题转回去,接着继续讲转换成模型的问题。
现在数据已经有我需要的东东了。(key
type
)
现在做创建对象
和赋值的操作。
extension NSObject {
...
class func createObj(dict:Dictionary<String,Any>) -> Self {
let obj = self.init()//创建对象
//赋值操作
return obj
}
}
那么赋值操作步骤:
获取key
type
关联关系,上面我们就已经获取到了。
那么获取后使用KVC进行赋值。
extension NSObject {
...
func variables() -> [VarData] {
let mirr = Mirror(reflecting: self)
//Mirror 的 children 是一个 (label: String?, value: Any) 元组类型,表示该类的所有属性的名字和类型
var keys = [VarData]()
keys.append(contentsOf:self.categroy(mirr))
debugPrint(keys)
return keys
}
...
/**根据我的项目需求 服务器key 和模型key 大小写不等。。。
* 此方法就过滤大小写不等的问题。
*/
private func ignoreCase(dict:Dictionary<String,Any>,key:String) -> Any? {
if let value = dict[key] {
return value
} else if let value = dict[key.uppercased()] {
return value
} else if let value = dict[key.lowercased()] {
return value
} else {
let dks = dict.keys.filter({ (key2) -> Bool in
if key.uppercased() == key2.uppercased() {
return true
} else if key.lowercased() == key2.lowercased() {
return true
}
return false
})
if dks.count == 1 {
if let value = dict[dks.first!] {
return value
}
}
}
return nil
}
func pm_setValuesForKeys(_ dict:[String:Any]) {
let vars = self.variables()
for v in vars {
switch v.1 {
case .null:
break
case.unknown:
break
case .number:
if let value = ignoreCase(dict:dict,key:v.0) as? String {
let decimal = NSDecimalNumber(string: value)
if decimal != NSDecimalNumber.notANumber {
self.setValue(decimal, forKey: v.0)
}
} else {
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(value, forKey: v.0)
}
}
}
case .string:
if let value = ignoreCase(dict:dict,key:v.0) as? NSNumber {
self.setValue(value.stringValue, forKey: v.0)
} else {
if let value = ignoreCase(dict:dict,key:v.0) as? String {
self.setValue(value, forKey: v.0)
} else {
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(String(describing: ignoreCase(dict:dict,key:v.0)), forKey: v.0)
}
}
}
}
break
default:
if let value = ignoreCase(dict:dict,key:v.0) {
if !(value is NSNull) {
self.setValue(value, forKey: v.0)
}
}
}
}
}
class func createObj(dict:Dictionary<String,Any>) -> Self {
let obj = self.init() //创建对象
obj.pm_setValuesForKeys(dict) //赋值操作
return obj
}
}
那么我们字典转换模型大概就这样完成了。
但是呢~~运行后会奔溃,这是为什么呢。
[ObjectConversionTests.ObjectConversionTests testModel] : failed: caught "NSUnknownKeyException", "[<ObjectConversion.Person 0x6000000a1920> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key height."
在Swift4.0这句话说找不到对应key
。但是我利用Mirror
获取到key
了呀。。What?
然后我发现Swift3.0 和 Swift4.0 模型的变量定义是不一样的。
Swift4.0是需要加@objc前缀修饰。
class Person : NSObject {
@objc var name:String = ""
@objc var age:Int = 0
@objc var desc:String?
@objc var height:Double = 0
}
然后在重新运行,就运行成功了。
在Swift3.0是不需要加的哦,具体为啥这里就不解释了=,=。
做到这一步我们可以简单的赋值了,但是还有Model
里面变量Model
的赋值还没做。
下集分晓。。。刚好有活干了 T_T。