• 【KakaJSON手册】01_JSON转Model_01_基本用法


    在iOS开发中,后台返回的数据大多是JSON格式,对应地会被网络框架层解析成Swift中的Dictionary、Array。由于数据类型的复杂、字段的繁多,直接使用Dictionary、Array会比较麻烦,比如items[0]["user"]["name"]这样的使用方式,非常不友善,而且没有智能语法提示。所以很多时候会考虑将JSON转换成Model之后再进行操作,会友善很多,比如items[0].user.name

    • Swift内置了一套Codable机制,可以用于JSON转Model。对于一些简单的模型结构,还是挺好用,但一旦牵扯到复杂的模型结构、一些个性化的需求(比如KeyMapping、类型不匹配时的转换处理、自定义解析规则等),Codable就不太能友善地完成任务了。
    • 为了解决上述问题,我编写了一套纯Swift实现的JSON与Model互相转换的框架:KakaJSON,本人非常喜欢龙珠,框架取名自Kaka Rotto(卡卡罗特,孙悟空)
    • KakaJSON通过了大量的单元测试用例(目前有80多个测试用例,未来会增加到上百个测试用例,也非常欢迎大家提供各种应用场景和测试用例),应对各种常用的数据场景,对外提供了一些友善易用、扩展性强、可高度个性化定制需求的接口,内置了Metedata缓存等机制,加快转换速度。
    • 本教程是为了让大家能够快速上手KakaJSON,挖掘它内部的各种功能,发挥它的最大威力。未来也可能会推出一些源码分析的文章。
    • 本文是《KakaJSON手册》系列文章的第一篇

    最简单的Model

    import KakaJSON
    
    // ① 让模型类型遵守`Convertible`协议
    struct Cat: Convertible {
        var name: String = ""
        var weight: Double = 0.0
    }
    
    // json也可以是NSDictionary、NSMutableDictionary类型
    let json: [String: Any] = [
        "name": "Miaomiao",
        "weight": 6.66
    ]
    
    // ② 直接调用json的model方法,传入模型类型,返回模型实例
    let cat1 = json.kj.model(Cat.self)
    XCTAssert(cat1.name == "Miaomiao")
    XCTAssert(cat1.weight == 6.66)
    
    // 或者也可以调用一个全局函数来完成JSON转模型
    let cat2 = model(from: json, Cat.self)
    

    Type Variable

    // 有时类型可能是个变量,比如
    var type: Convertible.Type = Cat.self
    
    // 调用带有type参数的方法即可
    // 由于传入的类型是Convertible.Type变量,因此返回值类型是Convertible,到时根据需求强制转换成自己想要的类型
    let cat1 = json.kj.model(type: type) as? Cat
    
    // 或者调用全局函数
    let cat2 = model(from: json, type: type) as? Cat
    

    Class类型

    class Cat: Convertible {
        var weight: Double = 0.0
        var name: String = ""
        // 由于Swift初始化机制的原因,`Convertible`协议强制要求实现init初始化器
        // 这样框架内部才可以完整初始化一个实例
        required init() {}
    }
    let json = ...
    let cat = json.kj.model(Cat.self)
    
    // 继承自NSObject的类也是一样的用法
    class Person: NSObject, Convertible {
        var name: String = ""
        var age: Int = 0
        // 由于NSObject内部已经有init,因此Person算是重载init,需再加上`override`
        required override init() {}
    }
    let person = json.kj.model(Person.self)
    
    struct Dog: Convertible {
        var weight: Double = 0.0
        var name: String = ""
        // 由于编译器自动帮结构体类型生成了一个init初始化器
        // 所以不需要自己再实现init初始化器
    }
    
    struct Pig: Convertible {
        var weight: Double
        var name: String
        // 如果没有在定义属性的同时指定初始值,编译器是不会为结构体生成init初始化器的
        // 所以需要自己实现init初始化器
        init() {
            name = ""
            weight = 0.0
        }
    }
    

    继承

    // 有继承的情况也是照常使用即可
    
    class Person: Convertible {
        var name: String = ""
        var age: Int = 0
        required init() {}
    }
    
    class Student: Person {
        var score: Int = 0
        var no: String = ""
    }
    
    let json: [String: Any] = [
        "name": "jack",
        "age": 18,
        "score": 98,
        "no": "9527"
    ]
    
    let student = json.kj.model(Student.self)
    

    let属性

    // KakaJSON也支持let属性
    
    struct Cat: Convertible {
        // 测试表明:在真机release模式下,对数字类型的let限制比较严格
        // 值虽然修改成功了(可以打印Cat结构体发现weight已经改掉了),但get出来还是0.0
        // 所以建议使用`private(set) var`取代`let`
        private(set) var weight: Double = 0.0
        let name: String = ""
    }
    let json = ...
    let cat = json.kj.model(Cat.self)
    

    NSNull

    struct Cat: Convertible {
        var weight: Double = 0.0
        var name: String = "xx"
        var data: NSNull?
    }
    
    let json: [String: Any] = [
        "name": NSNull(),
        "weight": 6.6,
        "data": NSNull()
    ]
    
    let cat = json.kj.model(Cat.self)
    // 转换失败,保留默认值
    XCTAssert(cat.name == "xx")
    XCTAssert(cat.weight == 6.6)
    XCTAssert(cat.data == NSNull())
    

    JSONString

    // jsonString也可以是NSString、NSMutableString类型
    let jsonString = """
    {
        "name": "Miaomiao",
        "weight": 6.66
    }
    """
    
    // 跟JSON的用法是一样的
    let cat1 = jsonString.kj.model(Cat.self)
    let cat2 = model(from: jsonString, Cat.self)
    
    var type: Convertible.Type = Cat.self
    let cat3 = jsonString.kj.model(type: type) as? Cat
    let cat4 = model(from: jsonString, type: type) as? Cat
    

    JSONData

    // jsonData也可以是NSData、NSMutableData类型
    let jsonData = """
    {
        "name": "Miaomiao",
        "weight": 6.66
    }
    """.data(using: .utf8)!
    
    // 跟JSON的用法是一样的
    let cat1 = jsonData.kj.model(Cat.self)
    let cat2 = model(from:jsonData, Cat.self)
    
    var type: Convertible.Type = Cat.self
    let cat3 = jsonData.kj.model(type: type) as? Cat
    let cat4 = model(from: jsonData, type: type) as? Cat
    

    Model嵌套1

    // 让需要进行转换的模型都遵守`Convertible`协议
    
    struct Book: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    struct Car: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    struct Dog: Convertible {
        var name: String = ""
        var age: Int = 0
    }
    
    struct Person: Convertible {
        var name: String = ""
        var car: Car?
        var books: [Book]?
        var dogs: [String: Dog]?
    }
    
    let json: [String: Any] = [
        "name": "Jack",
        "car": ["name": "BMW7", "price": 105.5],
        "books": [
            ["name": "Fast C++", "price": 666.6],
            ["name": "Data Structure And Algorithm", "price": 1666.6]
        ],
        "dogs": [
            "dog0": ["name": "Larry", "age": 5],
            "dog1": ["name": "ErHa", "age": 2]
        ]
    ]
    
    // 也是如此简单,不用再做额外的操作
    let person = json.kj.model(Person.self)
    XCTAssert(person.car?.name == "BMW7")
    XCTAssert(person.books?[1].name == "Data Structure And Algorithm")
    XCTAssert(person.dogs?["dog0"]?.name == "Larry")
    

    Model嵌套2

    // Set也能像Array那样支持Model嵌套
    
    // Set要求存放的元素遵守Hashable协议
    struct Book: Convertible, Hashable {
        var name: String = ""
        var price: Double = 0.0
    }
    
    struct Person: Convertible {
        var name: String = ""
        var books: Set<Book>?
    }
    
    let json: [String: Any] = [
        "name": "Jack",
        "books": [
            ["name": "Fast C++", "price": 666.6]
        ]
    ]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.name == "Jack")
    
    XCTAssert(person.books?.count == 1)
    // 从Set中取出来是个Book模型
    let book = person.books?.randomElement()
    XCTAssert(book?.name == "Fast C++")
    XCTAssert(book?.price == 666.6)
    

    Model嵌套3

    struct Car: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    class Dog: Convertible {
        var name: String = ""
        var age: Int = 0
        required init() {}
        init(name: String, age: Int) {
            self.name = name
            self.age = age
        }
    }
    
    struct Person: Convertible {
        var name: String = ""
        // 如果你的模型有默认值,KakaJSON内部不会再创建新的模型
        // 会直接重复利用你创建的模型,节省内存分配和初始化的开销
        var car: Car = Car(name: "Bently", price: 106.5)
        var dog: Dog = Dog(name: "Larry", age: 5)
    }
    
    let json: [String: Any] = [
        "name": "Jake",
        "car": ["price": 305.6],
        "dog": ["name": "Wangwang"]
    ]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.name == "Jake")
    // 保留默认值
    XCTAssert(person.car.name == "Bently")
    // 从json解析过来的值
    XCTAssert(person.car.price == 305.6)
    // 从json解析过来的值
    XCTAssert(person.dog.name == "Wangwang")
    // 保留默认值
    XCTAssert(person.dog.age == 5)
    

    递归

    class Person: Convertible {
        var name: String = ""
        var parent: Person?
        required init() {}
    }
    
    let json: [String: Any] = [
        "name": "Jack",
        "parent": ["name": "Jim"]
    ]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.name == "Jack")
    XCTAssert(person.parent?.name == "Jim")
    

    泛型

    struct NetResponse<Element>: Convertible {
        let data: Element? = nil
        let msg: String = ""
        private(set) var code: Int = 0
    }
    
    struct User: Convertible {
        let id: String = ""
        let nickName: String = ""
    }
    
    struct Goods: Convertible {
        private(set) var price: CGFloat = 0.0
        let name: String = ""
    }
    
    let json1 = """
    {
        "data": {"nickName": "KaKa", "id": 213234234},
        "msg": "Success",
        "code" : 200
    }
    """
    let response1 = json1.kj.model(NetResponse<User>.self)
    XCTAssert(response1?.msg == "Success")
    XCTAssert(response1?.code == 200)
    XCTAssert(response1?.data?.nickName == "KaKa")
    XCTAssert(response1?.data?.id == "213234234")
    
    let json2 = """
    {
        "data": [
            {"price": "6199", "name": "iPhone XR"},
            {"price": "8199", "name": "iPhone XS"},
            {"price": "9099", "name": "iPhone Max"}
        ],
        "msg": "Success",
        "code" : 200
    }
    """
    let response2 = json2.kj.model(NetResponse<[Goods]>.self)
    XCTAssert(response2?.msg == "Success")
    XCTAssert(response2?.code == 200)
    XCTAssert(response2?.data?.count == 3)
    XCTAssert(response2?.data?[0].price == 6199)
    XCTAssert(response2?.data?[0].name == "iPhone XR")
    XCTAssert(response2?.data?[1].price == 8199)
    XCTAssert(response2?.data?[1].name == "iPhone XS")
    XCTAssert(response2?.data?[2].price == 9099)
    XCTAssert(response2?.data?[2].name == "iPhone Max")
    

    Model数组

    struct Car: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    // json数组可以是Array<[String: Any]>、NSArray、NSMutableArray
    let json: [[String: Any]] = [
        ["name": "Benz", "price": 98.6],
        ["name": "Bently", "price": 305.7],
        ["name": "Audi", "price": 64.7]
    ]
    
    // 调用json数组的modelArray方法即可
    let cars = json.kj.modelArray(Car.self)
    XCTAssert(cars[1].name == "Bently")
    
    // 同样的还有其他方式
    let cars2 = modelArray(from: json, Car.self)
    var type: Convertible.Type = Car.self
    let cars3 = json.kj.modelArray(type: type) as? [Car]
    let cars4 = modelArray(from: json, type: type) as? [Car]
    
    // 另外,jsonString转为Model数组,也是如此简单
    let jsonString = "...."
    let cars5 = jsonString.kj.modelArray(Car.self)
    let cars6 = modelArray(from: jsonString, Car.self)
    let cars7 = jsonString.kj.modelArray(type: type) as? [Car]
    let cars8 = modelArray(from: jsonString, type: type) as? [Car]
    

    字典嵌套Model数组

    struct Book: Convertible {
        var name: String = ""
        var price: Double = 0.0
    }
    
    struct Person: Convertible {
        var name: String = ""
        var books: [String: [Book?]?]?
    }
    
    let name = "Jack"
    let mobileBooks = [
        (name: "iOS", price: 10.5),
        (name: "Android", price: 8.5)
    ]
    let serverBooks = [
        (name: "Java", price: 20.5),
        (name: "Go", price: 18.5)
    ]
    
    let json: [String: Any] = [
        "name": name,
        "books": [
            "mobile": [
                ["name": mobileBooks[0].name, "price": mobileBooks[0].price],
                ["name": mobileBooks[1].name, "price": mobileBooks[1].price]
            ],
            "server": [
                ["name": serverBooks[0].name, "price": serverBooks[0].price],
                ["name": serverBooks[1].name, "price": serverBooks[1].price]
            ]
        ]
    ]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.name == name)
    let books0 = person.books?["mobile"]
    XCTAssert(books0??.count == mobileBooks.count)
    for i in 0..<mobileBooks.count {
        XCTAssert(books0??[i]?.name == mobileBooks[i].name);
        XCTAssert(books0??[i]?.price == mobileBooks[i].price);
    }
    let books1 = person.books?["server"]
    XCTAssert(books1??.count == serverBooks.count)
    for i in 0..<serverBooks.count {
        XCTAssert(books1??[i]?.name == serverBooks[i].name);
        XCTAssert(books1??[i]?.price == serverBooks[i].price);
    }
    

    Convert

    // 如果你想把JSON数据转换到原本已经创建好的模型实例上,可以使用convert方法
    
    struct Cat: Convertible {
        var name: String = ""
        var weight: Double = 0.0
    }
    
    let json: [String: Any] = [
        "name": "Miaomiao",
        "weight": 6.66
    ]
    
    var cat = Cat()
    // .kj_m是.kj的mutable版本,牵扯到修改实例本身都是.kj_m开头
    cat.kj_m.convert(json)
    XCTAssert(cat.name == "Miaomiao")
    XCTAssert(cat.weight == 6.66)
    

    监听

    // 有时候可能想在JSON转模型之前、之后做一些额外的操作
    // KakaJSON会在JSON转模型之前调用模型的kj_willConvertToModel方法
    // KakaJSON会在JSON转模型之后调用模型的kj_didConvertToModel方法
    
    struct Car: Convertible {
        var name: String = ""
        var age: Int = 0
        
        mutating func kj_willConvertToModel(from json: [String: Any]) {
            print("Car - kj_willConvertToModel")
        }
        
        mutating func kj_didConvertToModel(from json: [String: Any]) {
            print("Car - kj_didConvertToModel")
        }
    }
    
    let name = "Benz"
    let age = 100
    let car = ["name": name, "age": age].kj.model(Car.self)
    // Car - kj_willConvertToModel
    // Car - kj_didConvertToModel
    XCTAssert(car.name == name)
    XCTAssert(car.age == age)
    
    /*************************************************************/
    
    // 同样也支持类
    class Person: Convertible {
        var name: String = ""
        var age: Int = 0
        required init() {}
        
        func kj_willConvertToModel(from json: [String: Any]) {
            print("Person - kj_willConvertToModel")
        }
        
        func kj_didConvertToModel(from json: [String: Any]) {
            print("Person - kj_didConvertToModel")
        }
    }
    
    class Student: Person {
        var score: Int = 0
        
        override func kj_willConvertToModel(from json: [String: Any]) {
            // 如果有必要的话,可以调用super的实现
            super.kj_willConvertToModel(from: json)
            
            print("Student - kj_willConvertToModel")
        }
        
        override func kj_didConvertToModel(from json: [String: Any]) {
            // 如果有必要的话,可以调用super的实现
            super.kj_didConvertToModel(from: json)
            
            print("Student - kj_didConvertToModel")
        }
    }
    
    let name = "jack"
    let age = 10
    let score = 100
    let student = ["name": name, "age": age, "score": score].kj.model(Student.self)
    // Person - kj_willConvertToModel
    // Student - kj_willConvertToModel
    // Person - kj_didConvertToModel
    // Student - kj_didConvertToModel
    XCTAssert(student.name == name)
    XCTAssert(student.age == age)
    XCTAssert(student.score == score)
    
  • 相关阅读:
    新浪视频播放器站外调用代码
    豆瓣FLASHMP3播放器
    奇艺 代码
    优酷新版播放器站外调用代码详解
    优酷网视频播放器站外调用详解
    PHP数组的交集array_intersect(),array_intersect_assoc(),array_inter_key()函数的小问题
    给你的博客添加个看电影的频道
    fwrite(),fread()命令混用问题(有图有真相)
    PHP字符编码问题之GB2312 VS UTF8
    POJ 1637 Sightseeing tour
  • 原文地址:https://www.cnblogs.com/mjios/p/11352776.html
Copyright © 2020-2023  润新知