- 最开始的时候仍是使用
NSJSONSerialization
转成字典和数组来使用!- 后来苹果用
Swift
从新实现了JSONSerialization
能够避免用NSArray
和NSDictionary
来桥接,提升解析效率。- 随后不少三方
JSON
库相继出现,例如:SwiftyJSON
、HandyJSON
......等,请原谅我一直没有用过这些三方库,虽然有参考学习过,但我一直维护改进本身封装的JSON库,这期间虽然使用JSON
的简便性有所提升,但我仍然以为很麻烦。- 从
Swift 4.0
开始,苹果提供了Codable
协议和JSONDecoder
,JSONEncoder
,这一改进使得一众三方库黯然失色,将JSON
到模型的便捷方式提高到了一个新的高度,但这种方式仍是过于强硬,模型类型过于死板,稍有不慎就会由于类型不符致使解析失败,所以我将本身的JSON
库添加了Codable
协议支持,做为官方库的一个有益补充。- 在
Swift 4.2
的时候dynamicMemberLookup
(动态属性)的加入又给(软)JSON
模型加了一剂强心针。
{"name":"小王","age":5,"reads":["格林童话","安徒生童话"]}git
之前的三方JSON
库在将相对动态的json数据解析到模型的时候除了要写属性类型外,在构造方法中,也要写上对应的key,模型写起来很啰嗦,例如:github
struct User {
var name:String
var age:Int
var reads:[String]
init(_ json:JSON) {
name = json["name"].string
age = json["age"].int
reads = json["reads"].array
}
}
复制代码
属性越多,构造方法中写的也越多,相同的键字符串和变量名显得十分啰嗦json
自从苹果革命性的增长了Codable
协议,如今只须要写模型的属性,而没必要写构造方法swift
struct User: Codable {
var name:String
var age:Int
var reads:[String]
}
复制代码
解析的时候也十分简便后端
let user = try! JSONDecoder().decode(User.self, from: data)
复制代码
多是由于苹果做为大公司,有着极度严苛的编码规范和先后端协做标准,所以JSONDecoder
对类型的要求是十分严苛的,若是换成下面这段JSON
内容就会解析失败:数组
{"name":"小王","age":"5","reads":["格林童话","安徒生童话"]}服务器
这其中将age
的类型变成了字符串的"5"
与模型的Int类型不符,使得整个JSON都没法解析。实际的开发中先后端未必有那么严苛的规范,也不必定总能碰到靠谱的后端,所以常常由于各类类型不符缘由解析模型失败就很蛋疼。网络
好比说通常状况下后端给咱们返回的JSON都有几个固定字段判断返回结果可用性,例如:post
{
"errorCode":0,
"errorMessage":"成功",
"result":{}
}
复制代码
{
"errorCode":1,
"errorMessage":"缺乏参数",
"result":null
}
复制代码
在上面的例子中errorCode
是服务器返回的错误状态0
表示请求成功errorMessage
是错误状态提示文本,result
根据不一样的API接口,返回不一样格式的JSON
内容。 若是想写一个模型的话:学习
struct ResponseJSON: Codable {
var errorMessage: String
var errorCode: Int
var result: ???????
}
复制代码
这就尴尬了,result
属性的类型无法填,由于没法肯定此刻它该使用什么模型。 解决方法以下:
- 不使用模型,改用字典,但这样就回归原始,用起来不方便。
result
属性类型使用[String:Any]
,虽然当下使用了模型,但后面使用很不方便。ResponseJSON
使用泛型定义由调用网络请求接口预传入result
所需的模型- 定义一种动态类型保存未彻底解析的
JSON
,允许对应接口使用result
字段时懒解析到恰当的模型
其中,比较好的方法是3
和4
, 但方法3仍是有缺陷,相同的API返回的JSON
结构就必定相同么?可能有一个type
属性表示着另外一个属性是什么结构。这种时候,预传入泛型就无能为力了。
所以咱们须要定义一个符合Codable
协议的动态类型
来保存未彻底解析的JSON
, 在须要的时候能够懒解析成所需模型。
public enum JSON: Codable {
case object (Object)
case array (Array)
case string (String)
case number (Number)
case bool (Bool)
case null
case error (Error, ignore:[String])
}
复制代码
代码固然不止这点,详细内容能够参考(Basic.frameworks)中关于JSON的部分
ResponseJSON
定义成:struct ResponseJSON: Codable {
var errorMessage: String
var errorCode: Int
var result: JSON
}
复制代码
在须要的时候使用
let user = try! JSON.Decoder().decode(User.self, from: responseJSON.result)
复制代码
或者其余模型
JSON
)中object
、array
、string
、number
、bool
、null
这6个是咱们熟知的json数据类型,而error
是什么呢?为何要添加它?顾名思义,error
就是错误,添加一个错误类型是用来代替属性获取可选链。
error
,咱们的JSON
应该是这样使用的。var json:JSON = .....
let name = json.result?.list?[0].name?.string
复制代码
这种方式是利用可选链?
来传递属性,若是其中一个属性不存在,不会报错,而是获得一个nil
值,通常状况这样已经足够,但若是出现错误的json,想要排查问题就须要逐层查看或测试,错误信息在可选链的第几层彻底丢失。
如今加入了error (Error, ignore:[String])
类型,就无需使用可选链来传递属性了
var json:JSON = .....
let name = json.result.list[0].name.string
复制代码
当某个属性没法获取时,能够给Error类型,并且还能够传递下去,而且不会丢失错误信息。最终转换string或者int时,能够根据开发环境和生产环境,选择中断或者返回默认值!
JSONDecoder
是苹果官方提供的将二进制JSON解析成模型Model
的解析类JSON.Decoder
是(Basic.frameworks)中仿照官方方法,将二进制或JSON软模型解析成模型Model
的解析类
- 他们的做用类似,只是
JSON.Decoder
削弱了数据类型的强制要求,JSON.Decoder
提供了比官方JSONDecoder
更多的解析策略,JSON.Decoder
为了方便提供了全局策略默认值的修改。
Data
或String
能够经过官方的JSONDecoder
或(Basic.frameworks)中的JSON.Decoder
解析到实现Codable
协议的Model
模型Data
或String
能够经过(Basic.frameworks)中的JSON.Decoder
或解析到JSON
软模型、Model
模型或二者混合JSON
软模型能够经过(Basic.frameworks)中的JSON.Decoder
解析到Model
模型或混合模型。Model
模型或混合模型能够经过(Basic.frameworks)中的JSON.Encoder
系列化成JSON
软模型,或Data
二进制Model
模型或混合模型能够经过官方的JSONEncoder
系列化成Data
二进制JSON
软模型能够经过官方的JSONEncoder
或(Basic.frameworks)中的JSON.Encoder
系列化成Data
二进制
参见文章《纯Swift项目-HTTP(Basic.frameworks)》一文中的例子