有时候,服务器返回的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. 如果没有1\2,使用Student父类的ConvertibleConfig配置
4. 如果没有1\2\3,使用Student父类的父类的ConvertibleConfig配置
5. 如果没有1\2\3\4,使用Student父类的父类的父类的....ConvertibleConfig配置
6. 如果没有1\2\3\4\5,使用全局的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")

【KakaJSON手册】03_JSON转Model_03_key处理的更多相关文章

  1. 【KakaJSON手册】01_JSON转Model_01_基本用法

    在iOS开发中,后台返回的数据大多是JSON格式,对应地会被网络框架层解析成Swift中的Dictionary.Array.由于数据类型的复杂.字段的繁多,直接使用Dictionary.Array会比 ...

  2. 【KakaJSON手册】04_JSON转Model_04_值过滤

    在KakaJSON手册的第2篇文章中提过:由于JSON格式能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型 比如客户端想要的是Date类型,服务器返回的 ...

  3. 【KakaJSON手册】02_JSON转Model_02_数据类型

    由于JSON格式的能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型. 比如服务器返回的时间可能是个毫秒数1565480696,但客户端想要的是Date类 ...

  4. 【KakaJSON手册】05_JSON转Model_05_动态模型

    在上一篇文章中提到:有时候服务器返回的某个字段的内容类型可能是不确定的 当时给出的解决方案是实现kk_modelValue或者kk_didConvertToModel方法,根据实际需求自定义JSON的 ...

  5. 【KakaJSON手册】06_Model转JSON

    前面的文章介绍了如何利用KakaJSON进行JSON转Model,从这篇文章开始介绍如何将Model转成JSON 生成JSON和JSONString struct Car: Convertible { ...

  6. 【KakaJSON手册】08_其他用法

    除了完成JSON和Model的转换之外,KakaJSON内部还有很多实用的功能,有些也开放为public接口了 遍历属性 struct Cat { var age: Int = 0 let name: ...

  7. 【KakaJSON手册】07_Coding_归档_解档

    KakaJSON可以只用一行代码将常用数据进行归档\解档 后面代码中会用到 file 文件路径 // 文件路径(String或者URL都可以) let file = "/Users/mj/D ...

  8. FREERTOS 手册阅读笔记

    郑重声明,版权所有! 转载需说明. FREERTOS堆栈大小的单位是word,不是byte. 根据处理器架构优化系统的任务优先级不能超过32,If the architecture optimized ...

  9. JS魔法堂:不完全国际化&本地化手册 之 理論篇

    前言  最近加入到新项目组负责前端技术预研和选型,其中涉及到一个熟悉又陌生的需求--国际化&本地化.熟悉的是之前的项目也玩过,陌生的是之前的实现仅仅停留在"有"的阶段而已. ...

随机推荐

  1. k8s学习 - 概念 - ReplicationController

    k8s学习 - 概念 - ReplicationController 我们有了 pod,那么就需要对 pod 进行控制,就是同一个服务的 podv我需要启动几个?如果需要扩容了,怎么办?这里就有个控制 ...

  2. CDQZ集训DAY4 日记

    早上起来之后发现座位被zzh占了,得知座位改为先来后到,什么鬼…… 于是去了另一个有耳机的机房,然而并没有什么卵用. T1上来感觉很有意思,先切50分再说.T2好像是原题的说,切了原题30分后大胆猜测 ...

  3. windows登陆suse虚拟机

    vmware我还是比较偏向7.1.4版本,其他版本装在win7上似乎有点问题. windows平台下,使用vmware + opensuse的网络配置过程如下: 装完vm后,会在本地连接新创建两个新连 ...

  4. python对Excel的读取

    在python自动化中,经常会遇到对数据文件的操作,比如添加多名员工,但是直接将员工数据写在python文件中,不但工作量大,要是以后再次遇到类似批量数据操作还会写在python文件中吗? 应对这一问 ...

  5. 【06】Jenkins:Gitlab 自动触发构建以及钉钉通知

    写在前面的话 在某些时候,我们希望能够实现这样一个功能,当用户提交东西到 gitlab 上的时候,希望它能够自动触发构建,发布到我们需要的环境. 目前我们内部有做类似的需求:产品提交原型到 gitla ...

  6. 【DFS的分支限界】(例题-算式等式)

    不知道DFS的请滚去 这里瞅一眼再说. -分支限界- 基本概念: 类似于回溯法,也是一种在问题的解空间树T上搜索问题解的算法.但在一般情况下,分支限界法与回溯法的求解目标不同.回溯法的求解目标是找出T ...

  7. weblogic安装时检查监视器: 必须配置为至少显示 256 种颜色,实际空间未知→失败

    1.首先如果你出现的结果是[未通过],则设置DISPLAY环境变量. 按网上方法:export DISPLAY=:0.0 然后继续安装你的东西……若成功则恭喜你~ 若[失败],按网上方法让你去看日志 ...

  8. 浅谈ActiveMQ与使用

    一.什么是消息中间件 消息中间件顾名思义实现的就是在两个系统或两个客户端之间进行消息传送 二.什么是ActiveMQ ActiveMQ是一种开源的基于JMS(Java Message Servie)规 ...

  9. Storm基础知识学习

    概述 Storm是一个免费开源的分布式实时计算系统.Storm能轻松可靠地处理无界的数据流,就像Hadoop对数据进行批处理 编程模型 spout:数据读取数据.接收数据.将数据写出到blot bol ...

  10. c语言进阶3-有参函数

    一.       有参函数的定义 有参函数的定义格式如下: 类型标识符  函数名(形式参数表列) { 语句: } 如 void fun(int a,int b) { printf(“a+b=%d”,a ...