快速入门

JSON(JavaScript Object Notation)是应用在 JavaScript 语言上的数据格式,常用于网络数据交换和存储。Apple 在Foundation模块中集成了 JSON 格式数据的解析与生成方法,使得 JSON 数据可以快速简单地导入 Swift 代码和 Core Data 中。

本文将以一段 JSON 数据作为示例,着重讲述如何使用基类来处理 JSON 数据的导入。

JSON 数据分析

假设服务器返回了关于 Swift 商店的数据,包含了商店名称、商店地址、开业时间以及其分店信息:

{"shopName": "Swift Shop","address": "2022 Apple Avenue","openYear": 2019,"branches": [{"shopName": "Swift UI Experience","address": "2019 Apple Avenue","branch_manager": "Fiona"},{"shopName": "Codable Protocol","address": "2014 iOS Street","branch_manager": "Steven","closed": true}]
}

可以看到这个 JSON 数据除了branch_manager键,其他键命名均为小驼峰规范。第一层是由四个键值对组成的。在branches下,第二层是一个数组并且数据由三个或四个键值对组成,键closed是可选的,默认为false。值得注意的是,第一层和第二层的结构中都有键shopNameaddress

简单使用

如果不需要做任何优化或者没有进一步模型需求,那么有一个网站可以满足 JSON 转 Swift 对象的要求:QuickType。将该 JSON 数据复制到网站中即可生成对应的 Swift 模型代码和解析代码,还有其他高级选项可以进一步定制代码:

生成的代码如下:

// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
//   let shopData = try? newJSONDecoder().decode(Shop.self, from: jsonData)import Foundation// MARK: - Shop
class Shop: Codable {var shopName, address: Stringlet openYear: Intlet branches: [Branch]init(shopName: String, address: String, openYear: Int, branches: [Branch]) {self.shopName = shopNameself.address = addressself.openYear = openYearself.branches = branches}
}// MARK: - Branch
class Branch: Codable {let shopName, address, branchManager: Stringlet closed: Bool?enum CodingKeys: String, CodingKey {case shopName, addresscase branchManager = "branch_manager"case closed}init(shopName: String, address: String, branchManager: String, closed: Bool?) {self.shopName = shopNameself.address = addressself.branchManager = branchManagerself.closed = closed}
}

可以看到Branch类中比Shop类中多了一个enum CodingKeys...结构。这是因为 Swift 中,默认生成或解析数据的变量命名为小驼峰方式,而Branch中有一个branch_manager键不满足要求,所以需要用CodingKey协议下的枚举对这个键名进行映射,并且其他键名也需要在枚举中列出。

QuickType提供的功能已经可以满足大部分的需求了。如果不同层级下有许多键是重复的,这么做会使用大量的代码,既不美观又不够“聪明”。这种情况下,使用子类可以大大提高数据模型的拓展性,使用泛型处理数据会使代码变得更加轻巧。

进阶模型

根据上文的分析,可以先创建一个 BasicShop 类作为基类:

class BasicShop: Codable {var shopName: String = ""var address: String = ""init() {}
}

与上文的初始化方法不同,基类中通过对变量赋初值来简化init()函数。这一步对于子类的初始化是十分重要的。

根据上述定义的基类,先创建第一层级的模型Shop

class Shop: BasicShop {var openYear: Int = 0var branches: [Branch] = []private enum CodingKeys: String, CodingKey {case openYearcase branches}override init() {super.init()}required init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)openYear = try container.decode(Int.self, forKey: .openYear)branches = try container.decode([Branch].self, forKey: .branches)try super.init(from: decoder)}override public func encode(to encoder: Encoder) throws {var container = encoder.container(keyedBy: CodingKeys.self)try container.encode(openYear, forKey: .openYear)try container.encode(branches, forKey: .branches)try super.encode(to: encoder)}
}

同样的,子类变量需要赋初值,并重写了init()方法;在 Decoder 和 Encoder 的初始化方法中,还需要手动编码对应键值,并调用基类中Codable协议对应的初始化方法。这里注意到有一处与网站生成的代码不同,子类中CodingKeys枚举是必需的,因为在重写 Decoder 和 Encoder 时需要用到对应的映射。为了避免不必要的冲突,CodingKeys枚举被设为private私有的。

class Branch: BasicShop {var branchManager: Stringvar closed: Bool?private enum CodingKeys: String, CodingKey {case branchManager = "branch_manager"case closed}override init() {super.init()}required init(from decoder: Decoder) throws {let container = try decoder.container(keyedBy: CodingKeys.self)branchManager = try container.decode(String.self, forKey: .branchManager)closed = try container.decodeIfPresent(Bool.self, forKey: .closed)try super.init(from: decoder)}override public func encode(to encoder: Encoder) throws {var container = encoder.container(keyedBy: CodingKeys.self)try container.encode(branchManager, forKey: .branchManager)try container.encode(closed, forKey: .closed)try super.encode(to: encoder)}
}

值得注意的是,在解析closed键时,使用的是container.decodeIfPresent方法而不是container.decode方法,后者在无法找到键值时会抛出错误,无法正常进行解析

解析和生成 JSON 数据

解析 JSON 数据:

let jsonData = jsonString.data(using: .utf8)
let shopData = try? JSONDecoder().decode(Shop.self, from: jsonData!)

生成 JSON 数据:

let jsonData = try JSONEncoder().encode(shopData)
let jsonString = String(data: jsonData, encoding: .utf8)

参考

QuickType
Pietar-Jan Nefkens

Swift - 使用原生库进行 JSON 解析和生成相关推荐

  1. Golang json 解析与生成

    文章目录 1.解析 json 1.1 map[string]interface{} 存储 json 1.2 struct 存储 json 1.3 []map[string]interface{} 解析 ...

  2. iOS json解析 和生成json串

    1.生成json字符串 NSData * jsonData = [NSJSONSerialization dataWithJSONObject:detailDic options:0 error:ni ...

  3. python jsonpath库_Python json解析库jsonpath原理及使用示例

    jsonpath jsonpath 用于多层嵌套 json格式的 解析. pip install jsonpath JsonPath 描述 $ 根节点 @ 现行节点 .or[] 取子节点 n/a 取父 ...

  4. C++开源库:Json解析:JsonCpp实践

    1:下载jsonCpp 编译源码 https://blog.csdn.net/u013456468/article/details/124908853      https://blog.csdn.n ...

  5. ESP8266 学习 十一 ESP8266 JSON解析

    解析JSON格式信息是一个较为繁琐的工作,因此我们将借助解析Arduino – ESP8266平台中解析JSON格式信息的第三方库--ArduionJson库.该库是目前最受好评的解析JSON信息第三 ...

  6. cjson构建_[置顶] cJSON库(构建json与解析json字符串)-c语言

    一.c语言获取json中的数据. 1.先要有cJOSN库,两个文件分别是cJSON.c和cJSON.h. 2.感性认识 char * json = "{ \"json\" ...

  7. iOS开源JSON解析库MJExtension

    iOS中JSON与NSObject互转有两种方式:1.iOS自带类NSJSONSerialization 2.第三方开源库SBJSON.JSONKit.MJExtension.项目中一直用MJExte ...

  8. gson解析天气json_几种常用JSON解析库性能比较

    PS:公众号推文时间工作日早晨8点50分,周末下午3点30分,不见不散哈! 作者:飞污熊 xncoding.com/2018/01/09/java/jsons.html 本篇通过JMH来测试一下Jav ...

  9. 五十六、Java的json解析库Json-lib和Gson

    @Author:Runsen @Dater:2020/06/18 文章目录 JSON Json-lib Gson JSON JSON不管是在Web开发还是服务器开发中是相当常见的数据传输格式,一般情况 ...

最新文章

  1. Verlet-js JavaScript 物理引擎
  2. 洛谷【P2257】YY的GCD
  3. 低门槛彻底理解JavaScript中的深拷贝和浅拷贝
  4. 追寻终极数据库 - 事务/分析混合处理系统的交付挑战 (3)
  5. vivado 如何创建工程模式_用Tcl定制Vivado设计实现流程
  6. cin java_java基础语法
  7. 计算机win764位相机驱动,万能驱动助理win7 64位
  8. Prescan入门教程之避坑笔记:初学者初用
  9. eclipse常用快捷键和设置
  10. oracle常见的经典查询语句(一)
  11. 四阶龙格库塔法的基本思想_利用龙格库塔法求解质点运动方程
  12. 电路中的常见符号总结
  13. Subclass in C++ - C++ 中的子类
  14. BuildMost分享-全球最大的自贸区揭牌!建材外贸在非洲会有多大舞台?
  15. SitePoint播客#124:iPhone贪婪的声音
  16. 数值分析:数据的最小二乘拟合
  17. java基础知识复习(上半)
  18. 同构数怎么判断_编程:输入1-100以内的一个整数,判断这个数是否是同构数。输出不同的情况...
  19. qq在线模板html,qq错误模板sogoupy.ime
  20. 转载-FileZilla Server源码分析(1)

热门文章

  1. lemon 信息学 奥林匹克 测评 运行时错误 cena 已经用的少了
  2. 如何使用nginx搭建一个自己本地的域名如(mall.com)
  3. Go语言运行环境、IDE配置
  4. python 网络抓包
  5. OPPO A37m刷机包_OPPO A37m线刷包教程
  6. JAVA实现Shapefile等转DXF格式
  7. 【传智播客】Javaweb程序设计任务教程 黑马程序员 第7,8,9,10,11,12,13,14,15章 课后答案
  8. b360m能插HTML,终于可以愉快装第八代平台了,那么首发B360主板都有哪些?
  9. 数据库安全性概述及TCSEC/TDI安全性能指标
  10. 解决VMware安装Ubuntu20.04没有中文输入法