项目名称:HandyJSON

项目地址:https://github.com/alibaba/handyjson

背景

JSON是移动端开发常用的应用层数据交换协议。最常见的场景便是,客户端向服务端发起网络请求,服务端返回JSON文本,然后客户端解析这个JSON文本,再把对应数据展现到页面上。

但在编程的时候,处理JSON是一件麻烦事。在不引入任何轮子的情况下,我们通常需要先把JSON转为Dictionary,然后还要记住每个数据对应的Key,用这个Key在Dictionary中取出对应的Value来使用。这个过程我们会犯各种错误:

  • Key拼写错了;
  • 路径写错了;
  • 类型搞错了;
  • 没拿到值懵逼了;
  • 某一天和服务端约定的某个字段变更了,没能更新所有用到它的地方;
  • ...

为了解决这些问题,很多处理JSON的开源库应运而生。在Swift中,这些开源库主要朝着两个方向努力:

  1. 保持JSON语义,直接解析JSON,但通过封装使调用方式更优雅、更安全;
  2. 预定义Model类,将JSON反序列化为类实例,再使用这些实例;

对于1,使用最广、评价最好的库非 SwiftyJSON 莫属,它很能代表这个方向的核心。它本质上仍然是根据JSON结构去取值,使用起来顺手、清晰。但也正因如此,这种做法没能妥善解决上述的几个问题,因为Key、路径、类型仍然需要开发者去指定;

对于2,我个人觉得这是更合理的方式。由于Model类的存在,JSON的解析和使用都受到了定义的约束,只要客户端和服务端约定好了这个Model类,客户端定义后,在业务中使用数据时就可以享受到语法检查、属性预览、属性补全等好处,而且一旦数据定义变更,编译器会强制所有用到的地方都改过来才能编译通过,非常安全。这个方向上,开源库们做的工作,主要就是把JSON文本反序列化到Model类上了。这一类JSON库有 ObjectMapper、JSONNeverDie、HandyJSON 等。而 HandyJSON 是其中使用最舒服的一个库,本文将介绍用 HandyJSON 来进行Model和JSON间的互相转换。

为什么用HandyJSON

在Swift中把JSON反序列化到Model类,在HandyJSON出现以前,主要使用两种方式:

  1. 让Model类继承自NSObject,然后class_copyPropertyList()方法获取属性名作为Key,从JSON中取得Value,再通过Objective-C runtime支持的KVC机制为类属性赋值;如JSONNeverDie

  2. 支持纯Swift类,但要求开发者实现Mapping函数,使用重载的运算符进行赋值,如ObjectMapper

这两者都有显而易见的缺点。前者要求Model继承自NSObject,非常不优雅,且直接否定了用struct来定义Model的方式;后者的Mapping函数要求开发者自定义,在其中指明每个属性对应的JSON字段名,代码侵入大,且仍然容易发生拼写错误、维护困难等问题。

HandyJSON独辟蹊径,采用Swift反射+内存赋值的方式来构造Model实例,规避了上述两个方案遇到的问题。

把JSON转换为Model

简单类型

某个Model类想支持通过HandyJSON来反序列化,只需要在定义时,实现HandyJSON协议,这个协议只要求实现一个空的init()函数。

比如我们和服务端约定了一个Animal数据,里面有name/id/num字段,那么我们这样定义Animal类:

class Animal: HandyJSON {var name: String?var id: String?var num: Int?required init() {} // 如果定义是struct,连init()函数都不用声明;
}

然后假设我们从服务端拿到这样一个JSON文本:

let jsonString = "{\"name\":\"cat\",\"id\":\"12345\",\"num\":180}"

引入HandyJSON以后,我们就可以这样来做反序列化了:

if let animal = JSONDeserializer<Animal>.deserializeFrom(json: jsonString) {print(animal.name)print(animal.id)print(animal.num)
}

简单吧~

比较复杂的类型

HandyJSON支持在类定义里使用各种形式的基本属性,包括可选(?),隐式解包可选(!),数组(Array),字典(Dictionary),Objective-C基本类型(NSString、NSNumber),各种类型的嵌套([Int]?、[String]?、[Int]!、...)等等。比如下面这个看起来比较复杂的类型:

struct Cat: HandyJSON {var id: Int64!var name: String!var friend: [String]?var weight: Double?var alive: Bool = truevar color: NSString?
}

一样轻松转换:

let jsonString = "{\"id\":1234567,\"name\":\"Kitty\",\"friend\":[\"Tom\",\"Jack\",\"Lily\",\"Black\"],\"weight\":15.34,\"alive\":false,\"color\":\"white\"}"if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString) {print(cat.xxx)
}

嵌套的Model类

如果Model类中的某个属性是另一个自定义的Model类,那么只要那个Model类也实现了HandyJSON协议,就一样可以转换:

struct Component: HandyJSON {var aInt: Int?var aString: String?
}struct Composition: HandyJSON {var aInt: Int?var comp1: Component?var comp2: Component?
}let jsonString = "{\"num\":12345,\"comp1\":{\"aInt\":1,\"aString\":\"aaaaa\"},\"comp2\":{\"aInt\":2,\"aString\":\"bbbbb\"}}"if let composition = JSONDeserializer<Composition>.deserializeFrom(json: jsonString) {print(composition)
}

指定反序列化JSON中某个节点

有时候服务端返回给我们的JSON文本包含了大量的状态信息,和Model无关,比如statusCodedebugMessage等,或者有用的数据是在某个节点以下,那么我们可以指定反序列化哪个节点:

struct Cat: HandyJSON {var id: Int64!var name: String!
}// 服务端返回了这个JSON,我们想解析的只有data里的cat
let jsonString = "{\"code\":200,\"msg\":\"success\",\"data\":{\"cat\":{\"id\":12345,\"name\":\"Kitty\"}}}"// 那么,我们指定解析 "data.cat",通过点来表达路径
if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString, designatedPath: "data.cat") {print(cat.name)
}

有继承关系的Model类

如果某个Model类继承自另一个Model类,只需要这个父Model类实现HandyJSON协议就可以:

class Animal: HandyJSON {var id: Int?var color: String?required init() {}
}class Cat: Animal {var name: String?required init() {}
}let jsonString = "{\"id\":12345,\"color\":\"black\",\"name\":\"cat\"}"if let cat = JSONDeserializer<Cat>.deserializeFrom(json: jsonString) {print(cat)
}

自定义解析方式

HandyJSON还提供了一个扩展能力,就是允许自行定义Model类某个字段的解析Key、解析方式。我们经常会有这样的需求:

  • 某个Model中,我们不想使用和服务端约定的key作为属性名,想自己定一个;
  • 有些类型如enumtuple是无法直接从JSON中解析出来的,但我们在Model类中有这样的属性;

HandyJSON协议提供了一个可选的mapping()函数,我们可以在其中指定某个字段用什么Key、或者用什么方法从JSON中解析出它的值。如我们有一个Model类和一个服务端返回的JSON串:

class Cat: HandyJSON {var id: Int64!var name: String!var parent: (String, String)?required init() {}
}let jsonString = "{\"cat_id\":12345,\"name\":\"Kitty\",\"parent\":\"Tom/Lily\"}"

可以看到,Cat类的id属性和JSON文本中的Key是对应不上的;而对于parent这个属性来说,它是一个元组,做不到从JSON中的"Tom/Lily"解析出来。所以我们要定义一个Mapping函数来做这两个支持:

class Cat: HandyJSON {var id: Int64!var name: String!var parent: (String, String)?required init() {}func mapping(mapper: HelpingMapper) {// 指定 id 字段用 "cat_id" 去解析mapper.specify(property: &id, name: "cat_id")// 指定 parent 字段用这个方法去解析mapper.specify(property: &parent) { (rawString) -> (String, String) inlet parentNames = rawString.characters.split{$0 == "/"}.map(String.init)return (parentNames[0], parentNames[1])}}
}

就这样,HandyJSON完美地帮我们进行了JSON到Model类的转换。真是太Handy了。

把Model转换为JSON文本

HandyJSON还提供了把Model类序列化为JSON文本的能力,简直无情。

基本类型

如果只需要进行序列化,那么在定义Model类时,不需要做任何特殊的改动。任何一个类的实例,直接调用HandyJSON的序列化方法去序列化,就能得到JSON字符串了。

class Animal {var name: String?var height: Int?init(name: String, height: Int) {self.name = nameself.height = height}
}let cat = Animal(name: "cat", height: 30)
print(JSONSerializer.serializeToJSON(object: cat)!)
print(JSONSerializer.serializeToJSON(object: cat, prettify: true)!)

可以通过prettify参数来指定获得的是否是格式化后的JSON串。

复杂类型

即使Model类中有别的Model类啥的,都一样支持。

enum Gender: String {case Male = "male"case Female = "Female"
}struct Subject {var id: Int64?var name: String?init(id: Int64, name: String) {self.id = idself.name = name}
}class Student {var name: String?var gender: Gender?var subjects: [Subject]?
}let student = Student()
student.name = "Jack"
student.gender = .Female
student.subjects = [Subject(id: 1, name: "math"), Subject(id: 2, name: "English"), Subject(id: 3, name: "Philosophy")]print(JSONSerializer.serializeToJSON(object: student)!)
print(JSONSerializer.serializeToJSON(object: student, prettify: true)!)

总结

有了HandyJSON的支持,现在我们可以开心地在Swift中使用JSON了。这个库还在更新中,已经支持了Swift 2.2+, Swift 3.0+。如果大家有什么需求或者建议,快去 https://github.com/alibaba/handyjson 给作者提issue吧~~

转载于:https://www.cnblogs.com/crazyacking/p/5927909.html

阿里巴巴最新开源项目 - [HandyJSON] 在Swift中优雅地处理JSON相关推荐

  1. 2018年阿里巴巴重要开源项目汇总(持续更新中)...

    摘要: 云栖社区特在2018年年末,将阿里巴巴的一些重要的开源项目进行整理,希望对大家有所帮助. 开源展示了人类共同协作,成果分享的魅力,每一次技术发展都是站在巨人的肩膀上,技术诸多创新和发展往往就是 ...

  2. 人工智能科技成熟的11个Github上免费开源项目,很多电影中才有的场景应用到现实颠覆普通人的认知和想象

    人工智能科技成熟的11个Github上免费开源项目,很多电影中才有的场景应用到现实颠覆普通人的认知和想象. 全文大纲: PULSE - 该开源项目可以通过给图片增加像素点来实现去马赛克或高清化. De ...

  3. 转发 ---- 2018年阿里巴巴重要开源项目汇总(持续更新中)

    转发自segmentfault  https://segmentfault.com/a/1190000017346799 前端 1.数据驱动的高交互可视化图形语法 AntV - G2 G2 是一套基于 ...

  4. 云原生领域最新开源项目和大厂实践

    目前,中国对整个云原生领域贡献巨大,中国的 K8s contributors 在全球所有贡献者中排名第二,超过 10% 的 CNCF 会员来自中国,26% 的 Kubernetes 认证供应商来自中国 ...

  5. 阿里巴巴中间件开源项目盘点(持续更新)

    Apache Dubbo 是一款高性能.轻量级的开源Java RPC框架,是国内影响力最大.使用最广泛的开源服务框架之一,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动 ...

  6. 大家都收藏了的最新开源项目Top12!CV、NLP、机器学习一应俱全

    参加 2018 AI开发者大会,请点击大会官网 译者 | 林椿眄.Jane 责编 | Jane 出品 | AI科技大本营 [导读]作者整理了近期最新发布及更新的 12 个非常有学习和收藏意义的开源项目 ...

  7. 云+社区技术沙龙丨解析腾讯最新开源项目背后的技术栈

    12 月 21 日,由腾讯云云+社区和腾讯对外开源管理办公室联合主办的技术沙龙在深圳腾讯大厦成功举办.本期活动的主题为「腾讯开源技术」,多位来自腾讯的开源技术专家及工程师围绕 Kona JDK.Ten ...

  8. 腾讯最新开源项目登上GitHub热榜:QQ音乐等18项业务都在用,日均PV过亿

    乾明 发自 凹非寺  量子位 报道 | 公众号 QbitAI 腾讯又又又开源.这次是一个跨端框架,名为Hippy. 在GitHub上开源不到一周,便斩获近1200+星,一举登上趋势榜. 腾讯介绍称,H ...

  9. GitHub上下载量突破100000+阿里巴巴的开源项目

    阿里在开源方面非常重视,开源了很多优秀的项目,遍布数据库.分布式.中间件.服务器.前端等领域.甚至有的项目在某个领域一枝独秀,今天就来盘点一下阿里巴巴开源的 开源项目 今天小编就给大家把GitHub上 ...

最新文章

  1. Microbiome:城环所杨军组揭示随机过程主导亚热带河流微型真核浮游生物群落构建
  2. 计算机与十进制 教案,二进制与十进制间的转换教案
  3. MathType如何保存常用公式
  4. OpenCV中图像的BGR格式 Img对象的属性说明
  5. 批处理命令 For循环命令具体解释!
  6. MAT分析OOM问题
  7. .net core实践系列之短信服务-Api的SDK的实现与测试
  8. mobaxterm用alt键作为meta键的方法
  9. Intel处理器电源管理技术(2) - 概述 - EIST P-State Turbo Boost HWP
  10. mysql都有哪些数据库日志_MySQL数据库之MySQL都有哪些日志?分别都代表什么
  11. vue+ 下拉框有选择项 点击选项也拿到值就是页面不显示选中项
  12. pap认证失败_PPP 口令认证协议 (PAP) 的配置与故障排除
  13. VOSviewer软件研究热点分析
  14. 63 岁工程师的退休生活:开发一款计算器应用,22 天获 56800 下载!
  15. 使用elastic job 不分片任务加载失败的可能原因
  16. 解决递归求快速排序时间复杂度计算
  17. 维吉尼亚密码(Vigenere)
  18. VUE启动报错:Error: The project seems to require pnpm but it‘s not installed
  19. 房屋装修设计更显档次需要从几个方面入手
  20. 信息差副业小项目,高利润,新手日入500+

热门文章

  1. Linux常用的基本命令08
  2. 堪比“脏牛”!“脏管道” 漏洞可获得Linux 所有主流发行版本的root权限
  3. 奇安信代码安全实验室帮助Red Hat修复多个QEMU高危漏洞,获官方致谢
  4. Oracle EBS 两个严重漏洞可导致企业金融记录遭篡改
  5. VMware Cloud Director 被曝严重漏洞,可导致黑客接管企业服务器
  6. 因被曝严重漏洞,Avast紧急禁用其 JavaScript 引擎
  7. 阿里云IoT安全运营中心-Link SOC,安全运营托管功能发布
  8. Tomcat启动log:SLF4J: Class path contains multiple SLF4J bindings.
  9. jQuery对象与DOM对象之间的转换方法
  10. phpcms v9中某些默认的文件路径