【KakaJSON手冊】03_JSON轉Model_03_key處理
- 2019 年 10 月 3 日
- 筆記
有時候,伺服器返回的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")