swift3 模型转字典(JSON)

Posted WoodBear009

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了swift3 模型转字典(JSON)相关的知识,希望对你有一定的参考价值。

  项目中最近需要将一个复杂的对象转成JSON,于是就利用Mirror写了一个模型转字典的通用方法,应该可以满足大部分使用场景下的需求了

函数实现:

func convertToDictNesting(obj: Any, remainFeild: [String]? = nil, replace: (((label: String, value: Any)) -> (String, Any))? = nil) -> [String: Any] 
    var dict: [String: Any] = [:]
    var children: [Mirror.Child] = []
    if let superChildren = Mirror(reflecting: obj).superclassMirror?.children 
        children.append(contentsOf: superChildren)
    
    children.append(contentsOf: Mirror(reflecting: obj).children)
    for child in children 
        if let key = child.label 
            if let remainFeild = remainFeild, !remainFeild.contains(key) 
                continue
            
            let subMirror = Mirror(reflecting: child.value)
            if let displayStyle = subMirror.displayStyle, displayStyle == .optional 
                if subMirror.children.isEmpty 
                    continue
                
            
            //解析类型属性
            let subDict = convertToDictNesting(obj: child.value, remainFeild: remainFeild, replace: replace)
            if subDict.isEmpty 
                if let replaceReturn = replace?((key, child.value)) 
                    if !replaceReturn.0.isEmpty 
                        if let aryValue = replaceReturn.1 as? [Any] 
                            var dictAry: [Any] = []
                            for value in aryValue 
                                let subDict = convertToDictNesting(obj: value, remainFeild: remainFeild, replace: replace)
                                if subDict.isEmpty 
                                    dictAry.append(value)
                                 else 
                                    dictAry.append(subDict)
                                
                            
                            dict[replaceReturn.0] = dictAry
                         else 
                            dict[replaceReturn.0] = replaceReturn.1
                        
                    
                 else 
                    if let aryValue = child.value as? [Any] 
                        var dictAry: [Any] = []
                        for value in aryValue 
                            let subDict = convertToDictNesting(obj: value, remainFeild: remainFeild, replace: replace)
                            if subDict.isEmpty 
                                dictAry.append(value)
                             else 
                                dictAry.append(subDict)
                            
                        
                        dict[key] = dictAry
                     else 
                        dict[key] = child.value
                    
                
             else 
                //非基础数据类型暂时只支持label替换
                if let replace = replace?((key, child.value)) 
                    if !replace.0.isEmpty 
                        if let someDict = subDict["some"] 
                            dict[replace.0] = someDict
                         else 
                            dict[replace.0] = subDict
                        
                    
                 else 
                    if let someDict = subDict["some"] 
                        dict[key] = someDict
                     else 
                        dict[key] = subDict
                    
                
            
        
    
    return dict

使用示例

1.基本使用

enum Sex 
    case male
    case female


class People 
    let name: String
    let sex: Sex
    let age: Int
    init(name: String, sex: Sex = .male, age: Int) 
        self.name = name
        self.sex = sex
        self.age = age
    


class Teacher: People 
    let salary: Float
    var title: String?
    init(name: String, sex: Sex = .male, age: Int, salary: Float, title: String? = nil) 
        self.salary = salary
        self.title = title
        super.init(name: name, sex: sex, age: age)
    


class Student: People 
    let id: String
    init(name: String, sex: Sex = .male, age: Int, id: String) 
        self.id = id
        super.init(name: name, sex: sex, age: age)
    


class Class 
    let classId: String
    let name: String
    var teacher: Teacher?
    var students: [Student]?
    init(classId: String, name: String) 
        self.classId = classId
        self.name = name
    


let class1 = Class(classId: "1", name: "数学")
let mathTeacher = Teacher(name: "mm", age: 30, salary: 100)
mathTeacher.title = "特级教师"
class1.teacher = mathTeacher
let student1 = Student(name: "ww", age: 11, id: "1")
let student2 = Student(name: "zz", age: 11, id: "2")
let student3 = Student(name: "yy", age: 11, id: "3")
class1.students = [student1, student2, student3]

let class1Dict = convertToDictNesting(obj: class1)
print(class1Dict)

let class2 = Class(classId: "1", name: "语文")
let chineseTeacher = Teacher(name: "ll", age: 35, salary: 100)
class2.teacher = chineseTeacher
class2.students = [student1, student2]
let class2Dict = convertToDictNesting(obj: class2)
print(class2Dict)

2.remainFeild参数的使用

  在有些情况下我们可能不希望转换对象的所有属性,这时可以通过remainFeild参数进行属性的筛选
let class1Dict = convertToDictNesting(obj: class1, remainFeild: ["classId", "name", "teacher", "sex", "title", "some"])
print(class1Dict)
print("\\n")

上面的代码仅转换了classId、name、teacher、sex等几个remainFeild中声明的属性 说明:1.如果保留的属性中包含可选型属性,remainFeild中需要加入"some"字段,否则可选型属性无法解析          2.Class、Student、Teacher都有name属性,如果仅想保留Class的name字段,过滤掉Student、Teacher的name属性,目前是暂不支持的。其实代码上我这边有过实现,可以针对不同的对象单独设置过滤字段,但在实际使用中发现这样会让使用成本过高,一旦对象结构复杂或是嵌套很深时,使用者需要对每一个涉及的对象都进行过滤设置,十分麻烦,要考虑的细节太多,于是就保留下了这种相对“无脑”式的过滤方式。不过好在最终生成的是字典,多余的字段只要使用者不去主动访问,一般也不会造成什么影响

3.replace字段的使用

  目前转换后的字典规则是:key=属性名,value=属性值。但有些情况下我们可能需要对key进行重命名,或是对value进行类型的转换或加入一些处理,这种情况下则可以借助replace参数
let class1Dict = convertToDictNesting(obj: class1)  (origin) -> (String, Any) in
    switch origin.label 
    case "salary" :
        return ("salary", "\\(origin.value)元")
    case "sex" :
        if let sex = origin.value as? Sex 
            return ("sex", sex.rawValue)
        
        return origin
    case "classId" :
        return ("ClassID", origin.value)
    default :
        return origin
    

print(class1Dict)

说明:1.转换同样只认属性不认对象,比较"无脑",主要是我们的项目中没有这个需求,如果你有相应需求,可以在replace闭包中将对象(obj)返回,这样调用者就可以针对不同对象做更有针对性的转换了。

4.其他说明

1.计算型属性无法被解析转换 2.默认情况下nil是会被过滤掉的,但是如果你有一个属性声明为了!,如
class Class 
    var testString: String!
但自始至终你又没有给它赋过任何值,在这种情况下nil是无法被过滤掉的,不过如果出现了这种情况,你更应该去排查一下代码,为什么会有这种危险的情况出现

最后,字典转JSON

func toJSONString(dict: [AnyHashable: Any]) -> String 
        do 
            let data = try JSONSerialization.data(withJSONObject: dict, options: [])
            let str = String(data: data, encoding: .utf8) ?? ""
            return str
         catch _ 
            return ""
        
    

以上是关于swift3 模型转字典(JSON)的主要内容,如果未能解决你的问题,请参考以下文章

Swift3 JSON字符串转字典和字典转JSON字符串的实现

在 Swift 3 中将 JSON 字符串转换为字典

如何在swift3中将字典插入Set?

在 Swift 中将 JSON 字典导入核心数据

OC_YYModel字典转模型的几种详细用法

Swift下面字典(json)和模型的转换