• 【KakaJSON手册】03_JSON转Model_03_key处理


    有时候,服务器返回的JSON数据的key跟客户端模型的属性名可能不一致,比如客户端遵守驼峰规范叫做nickName,而服务器端返回的JSON可能叫做nick_name。这时候为了保证数据转换成功,就需要对模型属性名和JSON的key进行相应的映射。KakaJSON提供了简单易用的映射方式。

    最基本的用法

    struct Person: Convertible {
        var nickName: String = ""
        var mostFavoriteNumber: Int = 0
        var birthday: String = ""
        
        // 实现kj_modelKey方法
        // 会传入模型的属性`property`作为参数,返回值就是属性对应的key
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 根据属性名来返回对应的key
            switch property.name {
                
            // 模型的`nickName`属性 对应 JSON中的`nick_name`
            case "nickName": return "nick_name"
                
            // 模型的`mostFavoriteNumber `属性 对应 JSON中的`most_favorite_number `
            case "mostFavoriteNumber": return "most_favorite_number"
                
            // 模型剩下的其他属性,直接用属性名作为JSON的key(属性名和key保持一致)
            default: return property.name
                
            }
        }
    }
    
    let nick_name = "ErHa"
    let most_favorite_number = 666
    let birthday = "2011-10-12"
    
    let json: [String: Any] = [
        "nick_name": nick_name,
        "most_favorite_number": most_favorite_number,
        "birthday": birthday
    ]
    
    let student = json.kj.model(Person.self)
    XCTAssert(student.nickName == nick_name)
    XCTAssert(student.mostFavoriteNumber == most_favorite_number)
    XCTAssert(student.birthday == birthday)
    

    驼峰 -> 下划线

    struct Person: Convertible {
        var nickName: String = ""
        var mostFavoriteNumber: Int = 0
        var birthday: String = ""
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 由于开发中可能经常遇到`驼峰`映射`下划线`的需求,KakaJSON已经内置了处理方法
            // 直接调用字符串的underlineCased方法就可以从`驼峰`转为`下划线`
            // `nickName` -> `nick_name`
            return property.name.kj.underlineCased()
        }
    }
    
    let nick_name = "ErHa"
    let most_favorite_number = 666
    let birthday = "2011-10-12"
    
    let json: [String: Any] = [
        "nick_name": nick_name,
        "most_favorite_number": most_favorite_number,
        "birthday": birthday
    ]
    
    let student = json.kj.model(Person.self)
    XCTAssert(student.nickName == nick_name)
    XCTAssert(student.mostFavoriteNumber == most_favorite_number)
    XCTAssert(student.birthday == birthday)
    

    下划线 -> 驼峰

    struct Person: Convertible {
        var nick_name: String = ""
        var most_favorite_number: Int = 0
        var birthday: String = ""
        
        // KakaJSON也给字符串内置了camelCased方法,可以由`下划线`转为`驼峰`
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `nick_name` -> `nickName`
            return property.name.kj.camelCased()
        }
    }
    
    let nickName = "ErHa"
    let mostFavoriteNumber = 666
    let birthday = "2011-10-12"
    
    let json: [String: Any] = [
        "nickName": nickName,
        "mostFavoriteNumber": mostFavoriteNumber,
        "birthday": birthday
    ]
    
    let student = json.kj.model(Person.self)
    XCTAssert(student.nick_name == nickName)
    XCTAssert(student.most_favorite_number == mostFavoriteNumber)
    XCTAssert(student.birthday == birthday)
    

    继承

    // 子类可以继承父类的实现
    
    class Person: Convertible {
        var nickName: String = ""
        required init() {}
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `nickName` -> `nick_ame`
            return property.name.kj.underlineCased()
        }
    }
    
    class Student: Person {
        var mathScore: Int = 0
        // Person的kj_modelKey会继承下来给Student使用
        // `mathScore` -> `math_score`
    }
    
    let nick_ame = "Jack"
    let math_score = 96
    let json: [String: Any] = ["nick_name": nick_ame, "math_score": math_score]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.nickName == nick_ame)
    
    let student = json.kj.model(Student.self)
    XCTAssert(student.nickName == nick_ame)
    XCTAssert(student.mathScore == math_score)
    

    重写

    // 子类可以重写父类的kj_modelKey方法,在父类实现的基础上加一些自己的需求
    
    class Person: Convertible {
        var name: String = ""
        required init() {}
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `name` -> `_name_`
            return property.name == "name" ? "_name_" : property.name
        }
    }
    
    class Student: Person {
        var score: Int = 0
        
        override func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 调用了`super.kj_modelKey`,在父类的基础上加了对`score`的处理
            // `score` -> `_score_`,`name` -> `_name_`
            return property.name == "score" ? "_score_" : super.kj_modelKey(from: property)
        }
    }
    
    let name = "Jack"
    let score = 96
    let json: [String: Any] = ["_name_": name, "_score_": score]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.name == name)
    
    let student = json.kj.model(Student.self)
    XCTAssert(student.name == name)
    XCTAssert(student.score == score)
    

    重写 + 覆盖

    // 子类可以重写父类的kj_modelKey方法,并完全覆盖父类的实现
    class Person: Convertible {
        var name: String = ""
        required init() {}
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `name` -> `_name_`
            return property.name == "name" ? "_name_" : property.name
        }
    }
    
    class Student: Person {
        var score: Int = 0
        
        override func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 这里并没有调用`super. kj_modelKey`
            // 因此`score` -> `_score_`,`name` -> `name`
            return property.name == "score" ? "_score_" : property.name
        }
    }
    
    let personName = "Jack"
    let person = ["_name_": personName].kj.model(Person.self)
    XCTAssert(person.name == personName)
    
    let studentName = "Rose"
    let studentScore = 96
    let student = ["name": studentName,
                   "_score_": studentScore].kj.model(Student.self)
    XCTAssert(student.name == studentName)
    XCTAssert(student.score == studentScore)
    

    全局配置

    // 一旦有需要进行`驼峰` -> `下划线`的映射,有可能整个项目的所有模型都需要进行映射,毕竟整个项目的命名规范是统一的
    // 假设项目中有100多个模型,那么就需要实现100多次`kj_modelKey`方法,调用100多次underlineCased方法
    // KakaJSON内置了全局配置机制,可以1次配置,就能适用于所有的模型(不管是struct还是class,只要是遵守Convertible协议的模型)
    
    // 全局配置整个项目中只需要配置1次,建议在AppDelegate的didFinishLaunching中配置1次即可
    ConvertibleConfig.setModelKey { property in
        property.name.kj.underlineCased()
    }
    // ConvertibleConfig.setModelKey { $0.name.kj.underlineCased() }
    
    class Person: Convertible {
        var nickName: String = ""
        required init() {}
    }
    
    class Student: Person {
        var mathScore: Int = 0
    }
    
    struct Car: Convertible {
        var maxSpeed: Double = 0.0
        var name: String = ""
    }
    
    let nick_ame = "Jack"
    let math_score = 96
    let json: [String: Any] = ["nick_name": nick_ame, "math_score": math_score]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.nickName == nick_ame)
    
    let student = json.kj.model(Student.self)
    XCTAssert(student.nickName == nick_ame)
    XCTAssert(student.mathScore == math_score)
    
    let max_speed = 250.0
    let name = "Bently"
    let car = ["max_speed": max_speed, "name": name].kj.model(Car.self)
    XCTAssert(car.maxSpeed == max_speed)
    XCTAssert(car.name == name)
    

    局部配置

    // 也可以给某些特定的类型做配置
    
    // 局部配置
    // 由于Student继承自Person,所以给Person做的配置,能适用在Student身上
    ConvertibleConfig.setModelKey(for: [Person.self, Car.self]) {
        property in
        property.name.kj.underlineCased()
    }
    
    class Person: Convertible {
        var nickName: String = ""
        required init() {}
    }
    
    class Student: Person {
        var mathScore: Int = 0
    }
    
    struct Car: Convertible {
        var maxSpeed: Double = 0.0
        var name: String = ""
    }
    
    let nick_ame = "Jack"
    let math_score = 96
    let json: [String: Any] = ["nick_name": nick_ame, "math_score": math_score]
    
    let person = json.kj.model(Person.self)
    XCTAssert(person.nickName == nick_ame)
    
    let student = json.kj.model(Student.self)
    XCTAssert(student.nickName == nick_ame)
    XCTAssert(student.mathScore == math_score)
    
    let max_speed = 250.0
    let name = "Bently"
    let car = ["max_speed": max_speed, "name": name].kj.model(Car.self)
    XCTAssert(car.maxSpeed == max_speed)
    XCTAssert(car.name == name)
    

    配置示例1

    // 全局配置
    ConvertibleConfig.setModelKey { property in
        property.name.kj.underlineCased()
    }
    
    // Person配置
    ConvertibleConfig.setModelKey(for: Person.self) { property in
        // `name` -> `_name_`
        property.name == "name" ? "_name_" : property.name
    }
    
    // Student配置
    ConvertibleConfig.setModelKey(for: Student.self) { property in
        // `score` -> `_score_`,`name` -> `name`
        property.name == "score" ? "_score_" : property.name
    }
    
    class Person: Convertible {
        var name: String = ""
        required init() {}
    }
    
    class Student: Person {
        var score: Int = 0
    }
    
    struct Car: Convertible {
        var maxSpeed: Double = 0.0
        var name: String = ""
    }
    
    let personName = "Jack"
    let person = ["_name_": personName].kj.model(Person.self)
    XCTAssert(person.name == personName)
    
    let studentName = "Rose"
    let studentScore = 96
    let student = ["name": studentName,
                   "_score_": studentScore].kj.model(Student.self)
    XCTAssert(student.name == studentName)
    XCTAssert(student.score == studentScore)
    
    let max_speed = 250.0
    let name = "Bently"
    let car = ["max_speed": max_speed, "name": name].kj.model(Car.self)
    XCTAssert(car.maxSpeed == max_speed)
    XCTAssert(car.name == name)
    

    配置示例2

    // 全局配置
    ConvertibleConfig.setModelKey { property in
        property.name.kj.underlineCased()
    }
    
    // Person配置
    ConvertibleConfig.setModelKey(for: Person.self) { property in
        // `name` -> `_name_`
        property.name == "name" ? "_name_" : property.name
    }
    
    // Student配置
    ConvertibleConfig.setModelKey(for: Student.self) { property in
        // `score` -> `_score_`,`name` -> `name`
        property.name == "score" ? "_score_" : property.name
    }
    
    class Person: Convertible {
        var name: String = ""
        required init() {}
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 可以通过ConvertibleConfig获取Person当初的配置
            // `name` -> `_name_`
            return ConvertibleConfig.modelKey(from: property, Person.self)
        }
    }
    
    class Student: Person {
        var score: Int = 0
        
        override func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // `score` -> `score`,`name` -> `name`
            return property.name
        }
    }
    
    struct Car: Convertible {
        var maxSpeed: Double = 0.0
        var name: String = ""
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            // 可以通过ConvertibleConfig获取全局配置
            // `maxSpeed` -> `max_speed`
            // `name` -> `name`
            return ConvertibleConfig.modelKey(from: property)
        }
    }
    
    /*
     当配置了多处modelKey时,它们的优先级从高到低,如下所示(以Student类型为例)
     1. 使用Student的kj_modelKey的实现
     2. 如果没有1,使用Student的ConvertibleConfig配置
     3. 如果没有12,使用Student父类的ConvertibleConfig配置
     4. 如果没有123,使用Student父类的父类的ConvertibleConfig配置
     5. 如果没有1234,使用Student父类的父类的父类的....ConvertibleConfig配置
     6. 如果没有12345,使用全局的ConvertibleConfig配置
     */
    
    // Person、Student、Car都实现了kj_modelKey,因此使用kj_modelKey的实现
    
    let personName = "Jack"
    let person = ["_name_": personName].kj.model(Person.self)
    XCTAssert(person.name == personName)
    
    let studentName = "Rose"
    let studentScore = 96
    let student = ["name": studentName,
                   "score": studentScore].kj.model(Student.self)
    XCTAssert(student.name == studentName)
    XCTAssert(student.score == studentScore)
    
    let max_speed = 250.0
    let name = "Bently"
    let car = ["max_speed": max_speed, "name": name].kj.model(Car.self)
    XCTAssert(car.maxSpeed == max_speed)
    XCTAssert(car.name == name)
    

    复杂的key映射

    struct Toy: Convertible {
        var price: Double = 0.0
        var name: String = ""
    }
    
    struct Dog: Convertible {
        var name: String = ""
        var age: Int = 0
        var nickName: String?
        var toy: Toy?
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            switch property.name {
            // 对应dog["toy"]
            case "toy": return "dog.toy"
            // 对应data[1]["dog"]["name"]
            case "name": return "data.1.dog.name"
            // 会按顺序映射数组中的每一个key,直到成功为止
            // 先映射`nickName`,如果失败再映射`nick_name`
            // 如果失败再映射`dog["nickName"]`,如果失败再映射`dog["nick_name"]`
            case "nickName": return ["nickName", "nick_name", "dog.nickName", "dog.nick_name"]
            default: return property.name
            }
        }
    }
    
    let name = "Larry"
    let age = 5
    let nickName1 = "Jake1"
    let nickName2 = "Jake2"
    let nickName3 = "Jake3"
    let nickName4 = "Jake4"
    let toy = (name: "Bobbi", price: 20.5)
    
    let json: [String: Any] = [
        "data": [10, ["dog" : ["name": name]]],
        "age": age,
        "nickName": nickName1,
        "nick_name": nickName2,
        "dog": [
            "nickName": nickName3,
            "nick_name": nickName4,
            "toy": ["name": toy.name, "price": toy.price]
        ]
    ]
    
    let dog = json.kj.model(Dog.self)
    XCTAssert(dog.name == name)
    XCTAssert(dog.age == age)
    XCTAssert(dog.nickName == nickName1)
    XCTAssert(dog.toy?.name == toy.name)
    XCTAssert(dog.toy?.price == toy.price)
    
    /*-------------------------------------------------*/
    
    struct Team: Convertible {
        var name: String?
        var captainName: String?
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            switch property.name {
            case "captainName":     return "captain.name"
            default:                return property.name
            }
        }
    }
    
    let teamName = "V"
    let captainName = "Quentin"
    
    let json: [String: Any] = [
        "name": teamName,
        "captain.name": captainName,
    ]
    let team = json.kj.model(Team.self)
    XCTAssert(team.name == teamName)
    XCTAssert(team.captainName == captainName)
    
    /*-------------------------------------------------*/
    
    struct Model: Convertible {
        var valueA: String?
        var valueB: String?
        
        func kj_modelKey(from property: Property) -> ModelPropertyKey {
            switch property.name {
            case "valueA":          return "a.0.a"
            case "valueB":          return "b.0.b.0.b"
            default:                return property.name
            }
        }
    }
    
    let json: [String: Any] = [
        "a": [ "l", "u", "o" ],
        "b": [
            [ "b": [ "x", "i", "u" ] ]
        ]
    ]
    let model = json.kj.model(Model.self)
    XCTAssert(model.valueA == "l")
    XCTAssert(model.valueB == "x")
    
  • 相关阅读:
    初见QT---信号和槽(二)
    初见QT---信号和槽
    Python的那些事---数据分析(一)---NumPy基础
    初见QT---创建QPushButton按钮
    初见QT---QT creator常见快捷键使用
    PHP 反射 Reflection
    python 代码求阶乘
    Python中的计时器对象
    python websocket 再线聊天室的 Demo
    Tornado创建一个web服务
  • 原文地址:https://www.cnblogs.com/mjios/p/11361397.html
Copyright © 2020-2023  润新知