【KakaJSON手册】01_JSON转Model_01_基本用法
在iOS开发中,后台返回的数据大多是JSON格式,对应地会被网络框架层解析成Swift中的Dictionary、Array。由于数据类型的复杂、字段的繁多,直接使用Dictionary、Array会比较麻烦,比如items[0]["user"]["name"]这样的使用方式,非常不友善,而且没有智能语法提示。所以很多时候会考虑将JSON转换成Model之后再进行操作,会友善很多,比如items[0].user.name。
- Swift内置了一套Codable机制,可以用于JSON转Model。对于一些简单的模型结构,还是挺好用,但一旦牵扯到复杂的模型结构、一些个性化的需求(比如KeyMapping、类型不匹配时的转换处理、自定义解析规则等),Codable就不太能友善地完成任务了。
- 为了解决上述问题,我编写了一套纯Swift实现的JSON与Model互相转换的框架:KakaJSON,本人非常喜欢龙珠,框架取名自Kaka Rotto(卡卡罗特,孙悟空)
- KakaJSON通过了大量的单元测试用例(目前有80多个测试用例,未来会增加到上百个测试用例,也非常欢迎大家提供各种应用场景和测试用例),应对各种常用的数据场景,对外提供了一些友善易用、扩展性强、可高度个性化定制需求的接口,内置了Metedata缓存等机制,加快转换速度。
- 本教程是为了让大家能够快速上手KakaJSON,挖掘它内部的各种功能,发挥它的最大威力。未来也可能会推出一些源码分析的文章。
- 本文是《KakaJSON手册》系列文章的第一篇
最简单的Model
import KakaJSON
// ① 让模型类型遵守`Convertible`协议
struct Cat: Convertible {
var name: String = ""
var weight: Double = 0.0
}
// json也可以是NSDictionary、NSMutableDictionary类型
let json: [String: Any] = [
"name": "Miaomiao",
"weight": 6.66
]
// ② 直接调用json的model方法,传入模型类型,返回模型实例
let cat1 = json.kj.model(Cat.self)
XCTAssert(cat1.name == "Miaomiao")
XCTAssert(cat1.weight == 6.66)
// 或者也可以调用一个全局函数来完成JSON转模型
let cat2 = model(from: json, Cat.self)
Type Variable
// 有时类型可能是个变量,比如
var type: Convertible.Type = Cat.self
// 调用带有type参数的方法即可
// 由于传入的类型是Convertible.Type变量,因此返回值类型是Convertible,到时根据需求强制转换成自己想要的类型
let cat1 = json.kj.model(type: type) as? Cat
// 或者调用全局函数
let cat2 = model(from: json, type: type) as? Cat
Class类型
class Cat: Convertible {
var weight: Double = 0.0
var name: String = ""
// 由于Swift初始化机制的原因,`Convertible`协议强制要求实现init初始化器
// 这样框架内部才可以完整初始化一个实例
required init() {}
}
let json = ...
let cat = json.kj.model(Cat.self)
// 继承自NSObject的类也是一样的用法
class Person: NSObject, Convertible {
var name: String = ""
var age: Int = 0
// 由于NSObject内部已经有init,因此Person算是重载init,需再加上`override`
required override init() {}
}
let person = json.kj.model(Person.self)
struct Dog: Convertible {
var weight: Double = 0.0
var name: String = ""
// 由于编译器自动帮结构体类型生成了一个init初始化器
// 所以不需要自己再实现init初始化器
}
struct Pig: Convertible {
var weight: Double
var name: String
// 如果没有在定义属性的同时指定初始值,编译器是不会为结构体生成init初始化器的
// 所以需要自己实现init初始化器
init() {
name = ""
weight = 0.0
}
}
继承
// 有继承的情况也是照常使用即可
class Person: Convertible {
var name: String = ""
var age: Int = 0
required init() {}
}
class Student: Person {
var score: Int = 0
var no: String = ""
}
let json: [String: Any] = [
"name": "jack",
"age": 18,
"score": 98,
"no": "9527"
]
let student = json.kj.model(Student.self)
let属性
// KakaJSON也支持let属性
struct Cat: Convertible {
// 测试表明:在真机release模式下,对数字类型的let限制比较严格
// 值虽然修改成功了(可以打印Cat结构体发现weight已经改掉了),但get出来还是0.0
// 所以建议使用`private(set) var`取代`let`
private(set) var weight: Double = 0.0
let name: String = ""
}
let json = ...
let cat = json.kj.model(Cat.self)
NSNull
struct Cat: Convertible {
var weight: Double = 0.0
var name: String = "xx"
var data: NSNull?
}
let json: [String: Any] = [
"name": NSNull(),
"weight": 6.6,
"data": NSNull()
]
let cat = json.kj.model(Cat.self)
// 转换失败,保留默认值
XCTAssert(cat.name == "xx")
XCTAssert(cat.weight == 6.6)
XCTAssert(cat.data == NSNull())
JSONString
// jsonString也可以是NSString、NSMutableString类型
let jsonString = """
{
"name": "Miaomiao",
"weight": 6.66
}
"""
// 跟JSON的用法是一样的
let cat1 = jsonString.kj.model(Cat.self)
let cat2 = model(from: jsonString, Cat.self)
var type: Convertible.Type = Cat.self
let cat3 = jsonString.kj.model(type: type) as? Cat
let cat4 = model(from: jsonString, type: type) as? Cat
JSONData
// jsonData也可以是NSData、NSMutableData类型
let jsonData = """
{
"name": "Miaomiao",
"weight": 6.66
}
""".data(using: .utf8)!
// 跟JSON的用法是一样的
let cat1 = jsonData.kj.model(Cat.self)
let cat2 = model(from:jsonData, Cat.self)
var type: Convertible.Type = Cat.self
let cat3 = jsonData.kj.model(type: type) as? Cat
let cat4 = model(from: jsonData, type: type) as? Cat
Model嵌套1
// 让需要进行转换的模型都遵守`Convertible`协议
struct Book: Convertible {
var name: String = ""
var price: Double = 0.0
}
struct Car: Convertible {
var name: String = ""
var price: Double = 0.0
}
struct Dog: Convertible {
var name: String = ""
var age: Int = 0
}
struct Person: Convertible {
var name: String = ""
var car: Car?
var books: [Book]?
var dogs: [String: Dog]?
}
let json: [String: Any] = [
"name": "Jack",
"car": ["name": "BMW7", "price": 105.5],
"books": [
["name": "Fast C++", "price": 666.6],
["name": "Data Structure And Algorithm", "price": 1666.6]
],
"dogs": [
"dog0": ["name": "Larry", "age": 5],
"dog1": ["name": "ErHa", "age": 2]
]
]
// 也是如此简单,不用再做额外的操作
let person = json.kj.model(Person.self)
XCTAssert(person.car?.name == "BMW7")
XCTAssert(person.books?[1].name == "Data Structure And Algorithm")
XCTAssert(person.dogs?["dog0"]?.name == "Larry")
Model嵌套2
// Set也能像Array那样支持Model嵌套
// Set要求存放的元素遵守Hashable协议
struct Book: Convertible, Hashable {
var name: String = ""
var price: Double = 0.0
}
struct Person: Convertible {
var name: String = ""
var books: Set<Book>?
}
let json: [String: Any] = [
"name": "Jack",
"books": [
["name": "Fast C++", "price": 666.6]
]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == "Jack")
XCTAssert(person.books?.count == 1)
// 从Set中取出来是个Book模型
let book = person.books?.randomElement()
XCTAssert(book?.name == "Fast C++")
XCTAssert(book?.price == 666.6)
Model嵌套3
struct Car: Convertible {
var name: String = ""
var price: Double = 0.0
}
class Dog: Convertible {
var name: String = ""
var age: Int = 0
required init() {}
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
struct Person: Convertible {
var name: String = ""
// 如果你的模型有默认值,KakaJSON内部不会再创建新的模型
// 会直接重复利用你创建的模型,节省内存分配和初始化的开销
var car: Car = Car(name: "Bently", price: 106.5)
var dog: Dog = Dog(name: "Larry", age: 5)
}
let json: [String: Any] = [
"name": "Jake",
"car": ["price": 305.6],
"dog": ["name": "Wangwang"]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == "Jake")
// 保留默认值
XCTAssert(person.car.name == "Bently")
// 从json解析过来的值
XCTAssert(person.car.price == 305.6)
// 从json解析过来的值
XCTAssert(person.dog.name == "Wangwang")
// 保留默认值
XCTAssert(person.dog.age == 5)
递归
class Person: Convertible {
var name: String = ""
var parent: Person?
required init() {}
}
let json: [String: Any] = [
"name": "Jack",
"parent": ["name": "Jim"]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == "Jack")
XCTAssert(person.parent?.name == "Jim")
泛型
struct NetResponse<Element>: Convertible {
let data: Element? = nil
let msg: String = ""
private(set) var code: Int = 0
}
struct User: Convertible {
let id: String = ""
let nickName: String = ""
}
struct Goods: Convertible {
private(set) var price: CGFloat = 0.0
let name: String = ""
}
let json1 = """
{
"data": {"nickName": "KaKa", "id": 213234234},
"msg": "Success",
"code" : 200
}
"""
let response1 = json1.kj.model(NetResponse<User>.self)
XCTAssert(response1?.msg == "Success")
XCTAssert(response1?.code == 200)
XCTAssert(response1?.data?.nickName == "KaKa")
XCTAssert(response1?.data?.id == "213234234")
let json2 = """
{
"data": [
{"price": "6199", "name": "iPhone XR"},
{"price": "8199", "name": "iPhone XS"},
{"price": "9099", "name": "iPhone Max"}
],
"msg": "Success",
"code" : 200
}
"""
let response2 = json2.kj.model(NetResponse<[Goods]>.self)
XCTAssert(response2?.msg == "Success")
XCTAssert(response2?.code == 200)
XCTAssert(response2?.data?.count == 3)
XCTAssert(response2?.data?[0].price == 6199)
XCTAssert(response2?.data?[0].name == "iPhone XR")
XCTAssert(response2?.data?[1].price == 8199)
XCTAssert(response2?.data?[1].name == "iPhone XS")
XCTAssert(response2?.data?[2].price == 9099)
XCTAssert(response2?.data?[2].name == "iPhone Max")
Model数组
struct Car: Convertible {
var name: String = ""
var price: Double = 0.0
}
// json数组可以是Array<[String: Any]>、NSArray、NSMutableArray
let json: [[String: Any]] = [
["name": "Benz", "price": 98.6],
["name": "Bently", "price": 305.7],
["name": "Audi", "price": 64.7]
]
// 调用json数组的modelArray方法即可
let cars = json.kj.modelArray(Car.self)
XCTAssert(cars[1].name == "Bently")
// 同样的还有其他方式
let cars2 = modelArray(from: json, Car.self)
var type: Convertible.Type = Car.self
let cars3 = json.kj.modelArray(type: type) as? [Car]
let cars4 = modelArray(from: json, type: type) as? [Car]
// 另外,jsonString转为Model数组,也是如此简单
let jsonString = "...."
let cars5 = jsonString.kj.modelArray(Car.self)
let cars6 = modelArray(from: jsonString, Car.self)
let cars7 = jsonString.kj.modelArray(type: type) as? [Car]
let cars8 = modelArray(from: jsonString, type: type) as? [Car]
字典嵌套Model数组
struct Book: Convertible {
var name: String = ""
var price: Double = 0.0
}
struct Person: Convertible {
var name: String = ""
var books: [String: [Book?]?]?
}
let name = "Jack"
let mobileBooks = [
(name: "iOS", price: 10.5),
(name: "Android", price: 8.5)
]
let serverBooks = [
(name: "Java", price: 20.5),
(name: "Go", price: 18.5)
]
let json: [String: Any] = [
"name": name,
"books": [
"mobile": [
["name": mobileBooks[0].name, "price": mobileBooks[0].price],
["name": mobileBooks[1].name, "price": mobileBooks[1].price]
],
"server": [
["name": serverBooks[0].name, "price": serverBooks[0].price],
["name": serverBooks[1].name, "price": serverBooks[1].price]
]
]
]
let person = json.kj.model(Person.self)
XCTAssert(person.name == name)
let books0 = person.books?["mobile"]
XCTAssert(books0??.count == mobileBooks.count)
for i in 0..<mobileBooks.count {
XCTAssert(books0??[i]?.name == mobileBooks[i].name);
XCTAssert(books0??[i]?.price == mobileBooks[i].price);
}
let books1 = person.books?["server"]
XCTAssert(books1??.count == serverBooks.count)
for i in 0..<serverBooks.count {
XCTAssert(books1??[i]?.name == serverBooks[i].name);
XCTAssert(books1??[i]?.price == serverBooks[i].price);
}
Convert
// 如果你想把JSON数据转换到原本已经创建好的模型实例上,可以使用convert方法
struct Cat: Convertible {
var name: String = ""
var weight: Double = 0.0
}
let json: [String: Any] = [
"name": "Miaomiao",
"weight": 6.66
]
var cat = Cat()
// .kj_m是.kj的mutable版本,牵扯到修改实例本身都是.kj_m开头
cat.kj_m.convert(json)
XCTAssert(cat.name == "Miaomiao")
XCTAssert(cat.weight == 6.66)
监听
// 有时候可能想在JSON转模型之前、之后做一些额外的操作
// KakaJSON会在JSON转模型之前调用模型的kj_willConvertToModel方法
// KakaJSON会在JSON转模型之后调用模型的kj_didConvertToModel方法
struct Car: Convertible {
var name: String = ""
var age: Int = 0
mutating func kj_willConvertToModel(from json: [String: Any]) {
print("Car - kj_willConvertToModel")
}
mutating func kj_didConvertToModel(from json: [String: Any]) {
print("Car - kj_didConvertToModel")
}
}
let name = "Benz"
let age = 100
let car = ["name": name, "age": age].kj.model(Car.self)
// Car - kj_willConvertToModel
// Car - kj_didConvertToModel
XCTAssert(car.name == name)
XCTAssert(car.age == age)
/*************************************************************/
// 同样也支持类
class Person: Convertible {
var name: String = ""
var age: Int = 0
required init() {}
func kj_willConvertToModel(from json: [String: Any]) {
print("Person - kj_willConvertToModel")
}
func kj_didConvertToModel(from json: [String: Any]) {
print("Person - kj_didConvertToModel")
}
}
class Student: Person {
var score: Int = 0
override func kj_willConvertToModel(from json: [String: Any]) {
// 如果有必要的话,可以调用super的实现
super.kj_willConvertToModel(from: json)
print("Student - kj_willConvertToModel")
}
override func kj_didConvertToModel(from json: [String: Any]) {
// 如果有必要的话,可以调用super的实现
super.kj_didConvertToModel(from: json)
print("Student - kj_didConvertToModel")
}
}
let name = "jack"
let age = 10
let score = 100
let student = ["name": name, "age": age, "score": score].kj.model(Student.self)
// Person - kj_willConvertToModel
// Student - kj_willConvertToModel
// Person - kj_didConvertToModel
// Student - kj_didConvertToModel
XCTAssert(student.name == name)
XCTAssert(student.age == age)
XCTAssert(student.score == score)
【KakaJSON手册】01_JSON转Model_01_基本用法的更多相关文章
- 【KakaJSON手册】04_JSON转Model_04_值过滤
在KakaJSON手册的第2篇文章中提过:由于JSON格式能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型 比如客户端想要的是Date类型,服务器返回的 ...
- 【KakaJSON手册】08_其他用法
除了完成JSON和Model的转换之外,KakaJSON内部还有很多实用的功能,有些也开放为public接口了 遍历属性 struct Cat { var age: Int = 0 let name: ...
- 【KakaJSON手册】03_JSON转Model_03_key处理
有时候,服务器返回的JSON数据的key跟客户端模型的属性名可能不一致,比如客户端遵守驼峰规范叫做nickName,而服务器端返回的JSON可能叫做nick_name.这时候为了保证数据转换成功,就需 ...
- 【KakaJSON手册】06_Model转JSON
前面的文章介绍了如何利用KakaJSON进行JSON转Model,从这篇文章开始介绍如何将Model转成JSON 生成JSON和JSONString struct Car: Convertible { ...
- Linux-查看C语言手册及man的特殊用法
man命令可以查看c语言库函数的函数原型, 比如 $ man malloc 如果显示 "No manual entry for malloc", 则需要安装 "man-p ...
- 【KakaJSON手册】02_JSON转Model_02_数据类型
由于JSON格式的能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型. 比如服务器返回的时间可能是个毫秒数1565480696,但客户端想要的是Date类 ...
- 【KakaJSON手册】05_JSON转Model_05_动态模型
在上一篇文章中提到:有时候服务器返回的某个字段的内容类型可能是不确定的 当时给出的解决方案是实现kk_modelValue或者kk_didConvertToModel方法,根据实际需求自定义JSON的 ...
- 【KakaJSON手册】07_Coding_归档_解档
KakaJSON可以只用一行代码将常用数据进行归档\解档 后面代码中会用到 file 文件路径 // 文件路径(String或者URL都可以) let file = "/Users/mj/D ...
- Vagrant 手册之同步目录 - 基本用法
原文地址 - 概述 原文地址 - 基本用法 同步目录 Synced folder 支持在宿主机和客户机之间共享目录,从而允许你在宿主机的项目文件上工作,但是可以在客户机上编译并运行. 默认情况下,Va ...
随机推荐
- JVM(八):Java 对象模型
JVM(八):Java 对象模型 本文将学习对象是如何创建的,对象的内存布局,以及如何定位访问一个对象. 对象创建 当虚拟机碰到一个new指令时,首先检查指令参数能否在常量池中定位一个类的符号引用,并 ...
- 南京POC项目总结-采用ActiveMQ进行项目实现
刚来时搞不清楚POC什么意思,查了下在这也普及下:Proof Of Conception,概念证明. 背景: 属于中途介入.二次分包项目,之前的分包商做出的东西性能上稍微有些弱,架构上有些不合理. 设 ...
- 教你用Python制作微信好友背景墙
目录: 0 引言 1 环境 2 代码实现 3 后记 0 引言 前段时间,微信朋友圈开始出现了一种晒照片新形式,微信好友墙,即在一张大图片中展示出自己的所有微信好友的头像. 效果如下图,出于隐私考虑,这 ...
- 【题解】跳房子-C++
Description奶牛按不太传统的方式玩起小朋友玩的跳房子游戏,现给出一个5*%的由数字组成的网格.它们在格子中向前前跳,向后跳,向左跳,向右跳,跳到网格中另一个数字后,又这样继续跳(可能跳到某个 ...
- 【最小生成树之Prim算法】-C++
[最小生成树之Kruskal算法] 没有看过的可以先看↑,会更简单. [模板]最小生成树 这一篇博客主要是介绍另外一种算法:Prim算法. prim算法就好像是一棵"生成树"在慢慢 ...
- NOIP2018普及T4暨洛谷P5018 对称二叉树题解
题目链接:https://www.luogu.org/problemnew/show/P5018 花絮:这道题真的比历年的t4都简单的多呀,而且本蒟蒻做得出t4做不出t3呜呜呜... 这道题可以是一只 ...
- [小米OJ] 6. 交叉队列
思路: 大概思想如下: 1. 动态规划求解,构造dp[][] 二维数组: 2. 设dp[i][j], i 为 第一个字符串的第i个字母:j 为 第二个字符串的第j个字母 3. dp[i][j] 如果 ...
- websocket的加密和解密过程
加密: import struct msg_bytes = "the emperor has not been half-baked in the early days of the col ...
- 打包名命令:tar
将多个文件或目录包成一个大文件的命令功能,我们称它是一种"打包命令". tar的参数非常多,这里只列出几个常用的参数,更多的参数你可以自行man tar查询. [root@www ...
- MetInfo5.3管理员密码重置漏洞
点击忘记密码 下一步 输入已知用户名或者邮箱点击下一步用Burp拦截 右键发送到Repeater 在第一行php后面拼接?met_host虚拟机kali的ip地址:端口号拼接完成后 用虚拟机监听拼接的 ...