[HandyJSON] 在Swift语言中处理JSON - 转换JSON和Model
背景JSON是移动端开发常用的应用层数据交换协议。最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上。 但在编程的时候,处理JSON是一件麻烦事。在不引入任何轮子的情况下,我们通常需要先把JSON转为Dictionary,然后还要记住每个数据对应的Key,用这个Key在Dictionary中取出对应的Value来使用。这个过程我们会犯各种错误:
为了解决这些问题,很多处理JSON的开源库应运而生。在Swift中,这些开源库主要朝着两个方向努力:
对于1,使用最广、评价最好的库非SwiftyJSON莫属,它很能代表这个方向的核心。它本质上仍然是根据JSON结构去取值,使用起来顺手、清晰。但也正因如此,这种做法没能妥善解决上述的几个问题,因为Key、路径、类型仍然需要开发者去指定; 对于2,我个人觉得这是更合理的方式。由于Model类的存在,JSON的解析和使用都受到了定义的约束,只要客户端和服务端约定好了这个Model类,客户端定义后,在业务中使用数据时就可以享受到语法检查、属性预览、属性补全等好处,而且一旦数据定义变更,编译器会强制所有用到的地方都改过来才能编译通过,非常安全。这个方向上,开源库们做的工作,主要就是把JSON文本反序列化到Model类上了。这一类JSON库有ObjectMapper、JSONNeverDie、HandyJSON等。而HandyJSON是其中使用最舒服的一个库,本文将介绍用HandyJSON来进行Model和JSON间的互相转换。 为什么用HandyJSON 在Swift中把JSON反序列化到Model类,在
这两者都有显而易见的缺点。前者要求Model继承自 而 把JSON转换为Model简单类型 某个Model类想支持通过 比如我们和服务端约定了一个 class Animal: HandyJSON {
var name: String?
var id: var num: Int?
required init() {}
}
然后假设我们从服务端拿到这样一个JSON文本: let jsonString = "{"name":"cat","id"12345"num":180}"
引入 if let animal = JSONDeserializer<Animal>.deserializeFrom(json: jsonString) {
print(animal.name)
print(animal.id)
print(animal.num)
}
简单吧~ 支持Struct 如果Model的定义是struct,由于Swift中struct提供了默认构造函数,所以就不需要再实现空的 struct Animal: HandyJSON {
var name: String?
var id: String?
var num: Int?
}
let jsonString = "{"name":"cat","id":"12345num":180}"
if let animal = JSONDeserializer<Animal>.deserializeFrom(json: jsonString) {
print(animal)
}
比较复杂的类型 Cat: var id: Int64!
String!
var friend: [String]?
var weight: Double?
var alive: Bool = true
var color: NSString?
required init() {}
}
一样轻松转换: let jsonString = "{"id":1234567,"name":"Kitty"friend":["Tom"Jack"Lily"Black"],22)">"weight":15.34,22)">"alive":false,22)">"color"white"}"
if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString) {
print(cat.xxx)
}
嵌套的Model类 如果Model类中的某个属性是另一个自定义的Model类,那么只要那个Model类也实现了 class Component: HandyJSON {
var aInt: Int?
var aString: String?
required init() {}
}
class Composition: HandyJSON {
var comp1: Component?
var comp2: Component?
required init() {}
}
let jsonString = "{"num":12345,"comp1":{"aInt":1,"aString":"aaaaa"},"comp2":2,152)">":"bbbbb"}}"
if let composition = JSONDeserializer<Composition>.deserializeFrom(json: jsonString) {
print(composition)
}
指定反序列化JSON中某个节点 有时候服务端返回给我们的JSON文本包含了大量的状态信息,和Model无关,比如 class Cat: HandyJSON {
var name: String!
required init() {}
}
// 服务端返回了这个JSON,我们想解析的只有data里的cat
let jsonString = "{"code":200,"msg":"successdata":{"":{"idname":"Kitty"}}}"
// 那么,我们指定解析 "data.cat",通过点来表达路径
if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString,designatedPath: "data.cat") {
print(cat.name)
}
有继承关系的Model类 如果某个Model类继承自另一个 class Animal: HandyJSON {
var id: Int?
var color: String?
required init() {}
}
Cat: Animal {
var name: String?
required init() {}
}
let jsonString = "{"idcolor":"black"}"
Cat>.deserializeFrom(json: jsonString) {
cat)
}
自定义解析方式HandyJSON还提供了一个扩展能力,就是允许自行定义Model类某个字段的解析Key、解析方式。我们经常会有这样的需求:
HandyJSON协议提供了一个可选的 var name: String!
var parent: (String,String)?
required init() {}
}
let jsonString = "{"cat_idparent":"Tom/Lily"}"
可以看到, class Cat: HandyJSON {
var id: Int64!
name: String!
parent: (String,String)?
required init() {}
func mapping(mapper: HelpingMapper) {
// 指定 id 字段用 "cat_id" 去解析
mapper.specify(property: &id,name: "cat_id")
// 指定 parent 字段用这个方法去解析
mapper.specify(: &parent) { (rawString) -> (String) in
let parentNames = rawString.characters.split{$0 == "/"}.map(String.init)
return (parentNames[0],parentNames[1])
}
}
}
就这样, 把Model转换为JSON文本HandyJSON还提供了把Model类序列化为JSON文本的能力,简直无情。 基本类型 如果只需要进行序列化,那么在定义Model类时,不需要做任何特殊的改动。任何一个类的实例,直接调用 class Animal {
var name: String?
var height: Int?
init(name: String,height: Int) {
self.name = name
self.height = height
}
}
let cat = Animal(name: "cat",0)">t: 30)
// 序列化为简单JSON文本
if let jsonStr = JSONSerializer.serialize(model: cat).toJSON() {
print("simple json string: ",jsonStr)
}
// 序列化为格式化的JSON文本
let prettifyJSON = JSONSerializer.serialize(cat).toPrettifyJSON() {
"prettify json string: ",prettifyJSON)
}
// 序列化为简单字典
let dict = JSONSerializer.serialize(cat).toSimpleDictionary() {
"dictionary: ",dict)
}
复杂类型即使Model类中有别的Model类啥的,都一样支持。 enum Gender {
case Male
case Female
}
struct Subject {
Int64?
String?
init( Int64,210)">String) {
self.id = id
self.name = name
}
}
class Student {
String?
gender: Gender?
subjects: [Subject]?
}
let student = Student()
student.name = "Jack"
student.gender = .Female
student.subjects = [Subject( 1,name: "math"),Subject( 2,152)">"English"),152)"> 3,152)">"Philosophy")]
let jsonStr = JSONSerializer.serialize(model: student).toJSON() {
print(let prettifyJSON = JSONSerializer.serialize(model: student).toPrettifyJSON() {
print(let dict = JSONSerializer.serialize(model: student).toSimpleDictionary() {
print( 总结
|