有时候,服务器返回的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)