Swift - 使用反射将自定义对象数据序列化成JSON数据
(本文代码已升级至Swift4)
注意:由于Swift4新增了一个Codable协议可以很方便地实现对象序列化,现在将对象转成JSON数据不用这么麻烦了。具体做法见文章最下方部分。
但用其生成JSON数据有个限制:只能将Foundation对象转换成JSON。即顶层对象必须是Array或者Dictionary,所有的对象必须是String、Number、Array、Dictionary、Nil的实例。
所以,如果我们想要把自定义类型的数据对象转成JSON数据,JSONSerialization就无能为力了。
所以,如果我们想要把自定义类型的数据对象转成JSON数据,JSONSerialization就无能为力了。
1,将自定义对象转成JSON数据的实现原理(不借助第三方库)
(1)首先我们使用反射(Reflection)对自定义类型的数据对象中所有的属性进行递归遍历,生成字典类型的数据并返回。
(2)接着使用JSONSerialization就可以把这个字典类型的数据转换成jSON数据了。
2,比如我们自定义一个的联系人类
3,JSON串生成的功能实现
4,测试样例
//用户类 class User { var name:String = "" //姓名 var nickname:String? //昵称 var age:Int? //年龄 var emails:[String]? //邮件地址 var tels:[Telephone]? //电话 var createTime:Date = Date() //创建时间 } //电话结构体 struct Telephone { var title:String //电话标题 var number:String //电话号码 }
3,JSON串生成的功能实现
(1)首先定义一个叫JSON的协议,并对其扩展,实现协议中定义的两个方法。
toJSONModel():将数据转成可用的JSON模型。
toJSONString():将数据转成JSON字符串(其内部会调用前面的toJSONModel()方法)。
(2)将可选类型(Optional),自定义类(User、TeleTelephone),以及基本数据类型进行扩展,使其遵循JSON协议。
同时对于可选类型还要重写toJSONModel()方法,为的是当可选类型值不存在时返回nil,存在时将其转成具体类型并序列化。
(2)将可选类型(Optional),自定义类(User、TeleTelephone),以及基本数据类型进行扩展,使其遵循JSON协议。
同时对于可选类型还要重写toJSONModel()方法,为的是当可选类型值不存在时返回nil,存在时将其转成具体类型并序列化。
//自定义一个JSON协议 protocol JSON { func toJSONModel() -> AnyObject? func toJSONString() -> String? } //扩展协议方法 extension JSON { //将数据转成可用的JSON模型 func toJSONModel() -> AnyObject? { let mirror = Mirror(reflecting: self) if mirror.children.count > 0 { var result: [String:AnyObject] = [:] for case let (label?, value) in mirror.children { //print("属性:\(label) 值:\(value)") if let jsonValue = value as? JSON { result[label] = jsonValue.toJSONModel() } } return result as AnyObject } return self as AnyObject } //将数据转成JSON字符串 func toJSONString() -> String? { let jsonModel = self.toJSONModel() //利用OC的json库转换成Data, let data = try? JSONSerialization.data(withJSONObject: jsonModel!, options: []) //Data转换成String打印输出 let str = String(data: data!, encoding: .utf8) return str } } //扩展可选类型,使其遵循JSON协议 extension Optional: JSON { //可选类型重写toJSONModel()方法 func toJSONModel() -> AnyObject? { if let x = self { if let value = x as? JSON { return value.toJSONModel() } } return nil } } //扩展两个自定义类型,使其遵循JSON协议 extension User: JSON { } extension Telephone: JSON { } //扩展Swift的基本数据类型,使其遵循JSON协议 extension String: JSON { } extension Int: JSON { } extension Bool: JSON { } extension Dictionary: JSON { } //扩展Array,格式化输出 extension Array: JSON { //将数据转成可用的JSON模型 func toJSONModel() -> AnyObject? { var result: [AnyObject] = [] for value in self { if let jsonValue = value as? JSON , let jsonModel = jsonValue.toJSONModel(){ result.append(jsonModel) } } return result as AnyObject } } //扩展Date日期类型,格式化输出 extension Date: JSON { //将数据转成可用的JSON模型 func toJSONModel() -> AnyObject? { // 创建一个日期格式器 let dformatter = DateFormatter() // 为日期格式器设置格式字符串 dformatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss" // 使用日期格式器格式化日期、时间 let datestr = dformatter.string(from: self) return datestr as AnyObject } }
//创建一个User实例对象 let user1 = User() user1.name = "hangge" user1.age = 100 user1.emails = ["hangge@hangge.com","system@hangge.com"] //添加动画 let tel1 = Telephone(title: "手机", number: "123456") let tel2 = Telephone(title: "公司座机", number: "001-0358") user1.tels = [tel1, tel2] //输出json字符串 print(user1.toJSONString()!)看到控制台输出如下信息:

格式化后数据如下:
{
"tels": [
{
"number": "123456",
"title": "手机"
},
{
"number": "001-0358",
"title": "公司座机"
}
],
"createTime": "2016年09月01日 16:37:53",
"age": 100,
"name": "hangge",
"emails": [
"hangge@hangge.com",
"system@hangge.com"
]
}
附:通过 Codable 协议实现对象转 JSON 字符串
这里我们同样定义一个叫 JSON 的协议,只不过它继承了 Codable 协议。可以看到代码比上面的简单很多。//自定义一个JSON协议 protocol JSON: Codable { func toJSONString() -> String? } //扩展协议方法 extension JSON { //将数据转成可用的JSON模型 func toJSONString() -> String? { //encoded对象 if let encodedData = try? JSONEncoder().encode(self) { //从encoded对象获取String return String(data: encodedData, encoding: .utf8) } return nil } } //用户类 class User:JSON { var name:String = "" //姓名 var nickname:String? //昵称 var age:Int? //年龄 var emails:[String]? //邮件地址 var tels:[Telephone]? //电话 var createTime:Date = Date() //创建时间 } //电话结构体 struct Telephone:JSON { var title:String //电话标题 var number:String //电话号码 }
使用方法也和上面一样:
//创建一个User实例对象 let user1 = User() user1.name = "hangge" user1.age = 100 user1.emails = ["hangge@hangge.com","system@hangge.com"] //添加动画 let tel1 = Telephone(title: "手机", number: "123456") let tel2 = Telephone(title: "公司座机", number: "001-0358") user1.tels = [tel1, tel2] //输出json字符串 print(user1.toJSONString()!)
如果含有自定义数组数据,如何取消数组下标呢?
请教航哥, 我按照上述方法,为什么传递的参数,顺序会变化,并且数据会丢失?
let register = EnrollUser3(Phone: enroll.Phone, ACode: enroll.ACode, Psw: enroll.Psw, Loc: loc(Position: position(Lng: enroll.Lng, Lat: enroll.Lat), Time: enroll.Time!!, Addr: enroll.Add))
print(register)
let enrollInf = register.toJSONString()!
print(enrollInf)
控制台输出信息如下
EnrollUser3(Phone: "15021475725", ACode: "2557", Psw: "4C0FC85FA1F01624C8A4C4D938234D2D", Loc: HuoZhanApp.loc(Position: HuoZhanApp.position(Lng: 116.403, Lat: 39.913), Time: 2016-01-26 03:20:59 +0000, Addr: "北京市东城区广场西侧路"))
{"Phone":"15021475725","Loc":{"Position":{},"Addr":"北京市东城区广场西侧路"},"ACode":"2557","Psw":"4C0FC85FA1F01624C8A4C4D938234D2D"}
======
可以看到Loc顺序变到前边了,而且Position的数据和Time的数据都丢失了!
写的很好哦,最近在学习swift,会一直关注
十分感谢航哥的辛勤,受益颇多!谢谢您!