美文网首页
iOS使用KVC+Mirror实现字典和模型相互转换

iOS使用KVC+Mirror实现字典和模型相互转换

作者: 春风十月柔情 | 来源:发表于2019-08-13 18:17 被阅读0次

    GitHub地址

    一、字典转模型

    • 创建Test.json文件,将这个文件添加到工程中。为这个json文件加入数据:
    {
        "name" : "小明",
        "age" : 8,
        "isMerry" : false,
        "tools" : ["炭笔", "尺子", "橡皮擦"],
        "pet" : {
            "name" : "二哈",
            "age" : 2
        },
        "books" : [
            {
                "title" : "三国演义",
                "author" : "罗贯中"
            },
            {
                "title" : "红楼梦",
                "author" : "曹雪芹"
            },
            {
                "title" : "西游记",
                "author" : "吴承恩"
            }
        ],
        "phone" : {
            "brand" : "apple",
            "name" : "iphoneX",
            "price" : 9999,
            "isOld" : false
        }
    }
    
    
    • 为NSObject添加extension:
    extension NSObject {
        
        func setValuesForKeys(_ keyedValues: [String : Any]) {
            for (key, value) in keyedValues {
                setCustomValue(value, forKey: key)
            }
        }
        
        private func setCustomValue(_ value: Any?, forKey key: String) {
            if let dict = value as? [String : Any] {
                if let objClass = getClassWith(key) as? NSObject.Type {
                    let model = objClass.init()
                    model.setValuesForKeys(dict)
                    setValue(model, forKey: key)
                    return
                }
            } else if let array = value as? [[String : Any]] {
                if let objClass = getClassWith(key, isArray: true) as? NSObject.Type {
                    var resultArray = [NSObject]()
                    for dict in array {
                        let model = objClass.init()
                        model.setValuesForKeys(dict)
                        resultArray.append(model)
                    }
                    setValue(resultArray, forKey: key)
                    return
                }
            }
            setValue(value, forKey: key)
        }
        
        private func getClassWith(_ key: String, isArray: Bool = false) -> AnyClass? {
            let mirror = Mirror(reflecting: self)
            let children = mirror.children.filter { (child) -> Bool in
                return child.label == key
            }
            if let value = children.first?.value {
                var valueType = "\(Mirror(reflecting: value).subjectType)"
                if isArray {
                    valueType = valueType.replacingOccurrences(of: "Array<", with: "")
                    valueType = valueType.replacingOccurrences(of: ">", with: "")
                }
                if let projectName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
                    return NSClassFromString(projectName + "." + valueType)
                }
            }
            return nil
        }
        
    }
    
    • 增加三个model:
    class TestModel: NSObject {
        @objc var name = ""
        @objc var age = 0
        @objc var isMerry = false
        @objc var tools = [String]()
        @objc var pet = [String : Any]()
        @objc var books = [Book]()
        @objc var phone = Phone()
    }
    class Book: NSObject {
        @objc var title = ""
        @objc var author = ""
    }
    class Phone: NSObject {
        @objc var brand = ""
        @objc var name = ""
        @objc var price = 0
        @objc var isOld = false
    }
    
    • 验证:
    if let path = Bundle.main.path(forResource: "Test", ofType: "json") {
        do {
            let jsonData = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
            let result = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
            if let dict = result as? [String : Any] {
                let model = TestModel()
                model.setValuesForKeys(dict)
                print(model.name)
                print(model.age)
                print(model.isMerry)
                print("--------")
                print(model.tools)
                print("--------")
                print(model.pet)
                for book in model.books {
                    print("--------")
                    print(book.title)
                    print(book.author)
                }
                print("--------")
                print(model.phone.brand)
                print(model.phone.name)
                print(model.phone.price)
                print(model.phone.isOld)
            }
        } catch {
            print(error)
        }
    }
    
    • 控制台输出:
    小明
    8
    false
    --------
    ["炭笔", "尺子", "橡皮擦"]
    --------
    ["age": 2, "name": 二哈]
    --------
    三国演义
    罗贯中
    --------
    红楼梦
    曹雪芹
    --------
    西游记
    吴承恩
    --------
    apple
    iphoneX
    9999
    false
    
    • 如果是数组,可以这样:
    if let array = result as? [[String : Any]] {
        var resultArray = [TestModel]()
        for dict in array {
            let model = TestModel()
            model.setValuesForKeys(dict)
            resultArray.append(model)
        }
        print(resultArray)
    }
    // 更省事一点也可以利用泛型把这些封装起来
    

    二、模型转字典

    • 具体实现:
    func getDictWith(obj: Any) -> [String : Any] {
        var dict = [String : Any]()
        let mirror = Mirror(reflecting: obj)
        for item in mirror.children {
            guard let key = item.label else {
                continue
            }
            let value = item.value
            if let array = value as? [Any] {
                dict.updateValue(getArrayWith(array: array), forKey: key)
            } else if isModelWith(value) {
                dict.updateValue(getDictWith(obj: value), forKey: key)
            } else {
                dict.updateValue(value, forKey: key)
            }
        }
        return dict
    }
        
    func getArrayWith(array: [Any]) -> [Any] {
        var resultArray = [Any]()
        guard let first = array.first else {
            return array
        }
        if isModelWith(first) {
            for obj in array {
                let dict = getDictWith(obj: obj)
                resultArray.append(dict)
            }
            return resultArray
        } else {
            return array
        }
    }
        
    func isModelWith(_ value: Any) -> Bool {
        let valueType = "\(Mirror(reflecting: value).subjectType)"
        if let projectName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
            return NSClassFromString(projectName + "." + valueType) != nil
        }
        return false
    }
    
    • 验证:
    // 把刚刚创建的model拿来用
    let dict = getDictWith(obj: model)
    print(dict)
    
    • 控制台输出:
    [
        "tools": ["炭笔", "尺子", "橡皮擦"], 
        "phone": ["name": "iphoneX", "brand": "apple", "isOld": false, "price": 9999],
        "books": [["title": "三国演义", "author": "罗贯中"], ["author": "曹雪芹", "title": "红楼梦"], ["author": "吴承恩", "title": "西游记"]],
        "pet": ["age": 2, "name": 二哈],
        "isMerry": false,
        "name": "小明",
        "age": 8
    ]
    

    相关文章

      网友评论

          本文标题:iOS使用KVC+Mirror实现字典和模型相互转换

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