【KakaJSON手册】03_JSON转Model_03_key处理

一个人想着一个人 提交于 2019-11-27 12:38:25

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

最基本的用法

struct Person: Convertible {
    var nickName: String = ""
    var mostFavoriteNumber: Int = 0
    var birthday: String = ""

    // 实现kk_modelKey方法
    // 会传入模型的属性`property`作为参数,返回值就是属性对应的key
    func kk_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.kk.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 kk_modelKey(from property: Property) -> ModelPropertyKey {
        // 由于开发中可能经常遇到`驼峰`映射`下划线`的需求,KakaJSON已经内置了处理方法
        // 直接调用字符串的underlineCased方法就可以从`驼峰`转为`下划线`
        // `nickName` -> `nick_name`
        return property.name.kk.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.kk.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 kk_modelKey(from property: Property) -> ModelPropertyKey {
        // `nick_name` -> `nickName`
        return property.name.kk.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.kk.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 kk_modelKey(from property: Property) -> ModelPropertyKey {
        // `nickName` -> `nick_ame`
        return property.name.kk.underlineCased()
    }
}

class Student: Person {
    var mathScore: Int = 0
    // Person的kk_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.kk.model(Person.self)
XCTAssert(person?.nickName == nick_ame)

let student = json.kk.model(Student.self)
XCTAssert(student?.nickName == nick_ame)
XCTAssert(student?.mathScore == math_score)

重写

// 子类可以重写父类的kk_modelKey方法,在父类实现的基础上加一些自己的需求

class Person: Convertible {
    var name: String = ""
    required init() {}
    
    func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // `name` -> `_name_`
        return property.name == "name" ? "_name_" : property.name
    }
}

class Student: Person {
    var score: Int = 0
    // 由于是重写,按照Swift的规定,必须加上`override`
    override func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // 调用了`super.kk_modelKey`,在父类的基础上加了对`score`的处理
        // `score` -> `_score_`,`name` -> `_name_`
        return property.name == "score"
            ? "_score_"
            : super.kk_modelKey(from: property)
    }
}

let name = "Jack"
let score = 96
let json: [String: Any] = ["_name_": name, "_score_": score]

let person = json.kk.model(Person.self)
XCTAssert(person?.name == name)

let student = json.kk.model(Student.self)
XCTAssert(student?.name == name)
XCTAssert(student?.score == score)

重写 + 覆盖

// 子类可以重写父类的kk_modelKey方法,并完全覆盖父类的实现
class Person: Convertible {
    var name: String = ""
    required init() {}
    
    func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // `name` -> `_name_`
        return property.name == "name" ? "_name_" : property.name
    }
}

class Student: Person {
    var score: Int = 0
    
    override func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // 这里并没有调用`super. kk_modelKey`
          // 因此`score` -> `_score_`,`name` -> `name`
        return property.name == "score" ? "_score_" : property.name
    }
}

let personName = "Jack"
let person = ["_name_": personName].kk.model(Person.self)
XCTAssert(person?.name == personName)

let studentName = "Rose"
let studentScore = 96
let student = ["name": studentName,
               "_score_": studentScore].kk.model(Student.self)
XCTAssert(student?.name == studentName)
XCTAssert(student?.score == studentScore)

全局配置

// 一旦有需要进行`驼峰` -> `下划线`的映射,有可能整个项目的所有模型都需要进行映射,毕竟整个项目的命名规范是统一的
// 假设项目中有100多个模型,那么就需要实现100多次`kk_modelKey`方法,调用100多次underlineCased方法
// KakaJSON内置了全局配置机制,可以1次配置,就能适用于所有的模型(不管是struct还是class,只要是遵守Convertible协议的模型)

// 全局配置整个项目中只需要配置1次,建议在AppDelegate的didFinishLaunching中配置1次即可
ConvertibleConfig.setModelKey { property in
    property.name.kk.underlineCased()
}
// ConvertibleConfig.setModelKey { $0.name.kk.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.kk.model(Person.self)
XCTAssert(person?.nickName == nick_ame)

let student = json.kk.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].kk.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.kk.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.kk.model(Person.self)
XCTAssert(person?.nickName == nick_ame)

let student = json.kk.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].kk.model(Car.self)
XCTAssert(car?.maxSpeed == max_speed)
XCTAssert(car?.name == name)

配置示例1

// 全局配置
ConvertibleConfig.setModelKey { property in
    property.name.kk.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].kk.model(Person.self)
XCTAssert(person?.name == personName)

let studentName = "Rose"
let studentScore = 96
let student = ["name": studentName,
               "_score_": studentScore].kk.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].kk.model(Car.self)
XCTAssert(car?.maxSpeed == max_speed)
XCTAssert(car?.name == name)

配置示例2

// 全局配置
ConvertibleConfig.setModelKey { property in
    property.name.kk.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 kk_modelKey(from property: Property) -> ModelPropertyKey {
        // 可以通过ConvertibleConfig获取Person当初的配置
        // `name` -> `_name_`
        return ConvertibleConfig.modelKey(Person.self, property: property)
    }
}

class Student: Person {
    var score: Int = 0
    
    override func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // `score` -> `score`,`name` -> `name`
        return property.name
    }
}

struct Car: Convertible {
    var maxSpeed: Double = 0.0
    var name: String = ""
    
    func kk_modelKey(from property: Property) -> ModelPropertyKey {
        // 可以通过ConvertibleConfig获取全局配置
        // `maxSpeed` -> `max_speed`
        // `name` -> `name`
        return ConvertibleConfig.modelKey(property: property)
    }
}

/*
 当配置了多处modelKey时,它们的优先级从高到低,如下所示(以Student类型为例)
 ① 使用Student的kk_modelKey的实现
 ② 如果没有①,使用Student的ConvertibleConfig配置
 ③ 如果没有①②,使用Student父类的ConvertibleConfig配置
 ④ 如果没有①②③,使用Student父类的父类的ConvertibleConfig配置
 ⑤ 如果没有①②③④,使用Student父类的父类的父类的....ConvertibleConfig配置
 ⑥ 如果没有①②③④⑤,使用全局的ConvertibleConfig配置
 */

// Person、Student、Car都实现了kk_modelKey,因此使用kk_modelKey的实现

let personName = "Jack"
let person = ["_name_": personName].kk.model(Person.self)
XCTAssert(person?.name == personName)

let studentName = "Rose"
let studentScore = 96
let student = ["name": studentName,
               "score": studentScore].kk.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].kk.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 kk_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.kk.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)
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!