【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")