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,会一直关注
十分感谢航哥的辛勤,受益颇多!谢谢您!