纯Swift项目-JSON(Basic.frameworks)

Swift JSON 发展史

  1. 最开始的时候还是使用NSJSONSerialization转成字典和数组来使用!
  2. 后来苹果用Swift重新实现了JSONSerialization可以避免用NSArrayNSDictionary来桥接,提高解析效率。
  3. 随后很多三方JSON库相继出现,例如:SwiftyJSONHandyJSON......等,请原谅我一直没有用过这些三方库,虽然有参考学习过,但我一直维护改进自己封装的JSON库,这期间虽然使用JSON的简便性有所提高,但我仍然觉得很麻烦。
  4. Swift 4.0 开始,苹果提供了Codable协议和JSONDecoderJSONEncoder,这一改进使得一众三方库黯然失色,将JSON到模型的便捷方式提升到了一个新的高度,但这种方式还是过于强硬,模型类型过于死板,稍有不慎就会因为类型不符导致解析失败,因此我将自己的JSON库添加了Codable协议支持,作为官方库的一个有益补充。
  5. Swift 4.2 的时候dynamicMemberLookup(动态属性)的加入又给(软)JSON模型加了一剂强心针。

例子JSON

{"name":"小王","age":5,"reads":["格林童话","安徒生童话"]}

以前的三方JSON库的缺陷

以前的三方JSON库在将相对动态的json数据解析到模型的时候除了要写属性类型外,在构造方法中,也要写上对应的key,模型写起来很啰嗦,例如:

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
    }
}
复制代码

属性越多,构造方法中写的也越多,相同的键字符串和变量名显得十分啰嗦


JSONDecoder 的缺陷

自从苹果革命性的增加了Codable协议,现在只需要写模型的属性,而不必写构造方法

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都有几个固定字段判断返回结果可用性,例如:

{
    "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属性的类型没法填,因为无法确定此刻它该使用什么模型。 解决方法如下:

  1. 不使用模型,改用字典,但这样就回归原始,用起来不方便。
  2. result属性类型使用[String:Any],虽然当下使用了模型,但后面使用很不方便。
  3. ResponseJSON使用泛型定义由调用网络请求接口预传入result所需的模型
  4. 定义一种动态类型保存未完全解析的JSON,容许对应接口使用result字段时懒解析到恰当的模型

其中,比较好的方法是34, 但方法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)中objectarraystringnumberboolnull这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.Decoder 的区别

  • JSONDecoder 是苹果官方提供的将二进制JSON解析成模型Model的解析类
  • JSON.Decoder是(Basic.frameworks)中仿照官方方法,将二进制或JSON软模型解析成模型Model的解析类

如图所示,不同线条样式代表不同的数据流向来源

  1. 他们的作用相似,只是JSON.Decoder削弱了数据类型的强制要求,
  2. JSON.Decoder提供了比官方JSONDecoder更多的解析策略,
  3. JSON.Decoder为了方便提供了全局策略默认值的修改。
流向总结
  1. DataString可以通过官方的JSONDecoder或(Basic.frameworks)中的JSON.Decoder解析到实现Codable协议的Model模型
  2. DataString可以通过(Basic.frameworks)中的JSON.Decoder或解析到JSON软模型、Model模型或两者混合
  3. JSON软模型可以通过(Basic.frameworks)中的JSON.Decoder解析到Model模型或混合模型。
  4. Model模型或混合模型可以通过(Basic.frameworks)中的JSON.Encoder系列化成JSON软模型,或Data二进制
  5. Model模型或混合模型可以通过官方的JSONEncoder系列化成Data二进制
  6. JSON软模型可以通过官方的JSONEncoder或(Basic.frameworks)中的JSON.Encoder系列化成Data二进制

实际使用实例

参见文章《纯Swift项目-HTTP(Basic.frameworks)》一文中的例子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值