swift php json解析,Swift 4.0 | JSON数据的解析和编码
文 / 菲拉兔
自己撸的图
要求:
Platform: iOS8.0+
Language: Swift4.0
Editor: Xcode9
【问题补充2017-09-28】
最近我发现了一个问题:在Swift4.0中对JSON数据进行解析的时候,如果还用老的JSONSerialization类的话,会出现一个BUG:
问题: 比如我有一个NSObject的类叫Student,其中包含一个var name = ""属性,那么在以上方法解析JSON数据的过程中,name的值将不被写入,这应该是Swift4.0的一个BUG;
解决方法:
用其他的名字替代name字段(暂时发现只有对这个属性不起作用),例如var sname = "";
用JSONDecoder新的方式去解析;
Swift4.0以前和OC时代的JSON数据处理
Swift(1..<4)& Objective-C
Swift4.0以前的JSON解析/编码,和OC时代一样,都是通过NSJSONSerialization类的一些类方法进行处理的
JSON解析
struct GroceryProduct{
var name: String
var points: Int
var description: String
}
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
// JSON序列化
guard let json = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers),
let array = json as? [[String: Any]] else{
fatalError("`JSON Data Serialize Failed`")
}
// 数据整理
var products = [GroceryProduct]()
for dict in array {
products.append(GroceryProduct.init(name: dict["name"] as? String ?? "",
points: dict["points"] as? Int ?? 0,
description: dict["description"] as? String ?? ""))
}
print(products)
Note:Swift编程中官方推荐用Struct代替Class,因为不占存储空间,但在实际开发中,如果用Struct去存储解析出来的JSON数据,还是比较麻烦的,尤其是在JSON序列化方面。下面用Class代替Struct演示Class在JSON序列化过程中的方便之处。
// 数据解析和处理全封装在数据Model里,更体现封装性
class GroceryProduct: NSObject{
var name = ""
var points = 0
var descript = ""
override func setValue(_ value: Any?, forKey key: String) {
// 拦截并进行数据处理
if key == "points" {
points = (value as? Int) ?? 10
}
else{
super.setValue(value, forKey: key)
}
}
// 未定义key的处理
override func setValue(_ value: Any?, forUndefinedKey key: String) {
if key == "description" {
descript = value as? String ?? ""
}
}
}
// 数据整理
var products = [GroceryProduct]()
for dict in array {
let product = GroceryProduct()
product.setValuesForKeys(dict)
products.append(product)
}
print(products)
JSON编码
// JSON编码
struct GroceryProduct{
var name: String
var points: Int
var description: String
// 将对象中的属性-值转换为JSON字典
func JSONDictionary(ignored keys: [String] = []) -> [String: Any] {
var dictionary = [String: Any]()
let mirror = Mirror.init(reflecting: self)
for (key, value) in mirror.children {
guard let key = key else{
continue
}
guard !keys.contains(key) else{
continue
}
dictionary.updateValue(value, forKey: key)
}
return dictionary
}
}
// 需要编码的JSON Object
var jsonArray = [[String: Any]]()
for product in products {
jsonArray.append(product.JSONDictionary())
}
// 判断是否是合法的JSON Object
guard JSONSerialization.isValidJSONObject(jsonArray) else{
fatalError("`Not Validate JSON Object`")
}
// 对象编码为JSON Data,并解析为JSON Text
guard let data = try? JSONSerialization.data(withJSONObject: jsonArray, options: .prettyPrinted),
let jsonText = String(data: data, encoding: .utf8) else{
fatalError("`JSON Object Encode Failed`")
}
print(jsonText)
Note: 合法的JSON Object应满足:
顶层对象为数组或字典对象
数组/字典中的所有对象必须为字符串, 数字类型NSNumber, 数组, 字典, 或 NSNull
所有的字典keys为字符串
所有的数字对象不能为NaN 或 infinity
所以struct / class 对象在JSON编码过程中需要自己手动转换成字典/数组,才可以正确被编码为JSON Data,并转换为字符串,然后发给服务器。
Swift4.0中JSON的操作
Swift4.0中利用全新采用JSONDecoder/JSONEncoder类来实现JSON数据的解析和编码。
JSONDecoder
要将JSON Data解析成相应的数据模型,并匹配相应的属性-值,对应的Struct或Class类型要遵守Decodable协议
//Decodable只能解析,不能被编码
struct GroceryProduct: Decodable{
var name: String
var points: Int
var description: String
}
func swift4JSONParser() {
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
// 利用JSONDecoder来解析JSON Data,解析成[GroceryProduct].self数组类型
let decoder = JSONDecoder()
guard let products = try? decoder.decode([GroceryProduct].self, from: data) else{
fatalError("`JSON Decode Failed`")
}
print(products)
}
Custom Key Names
有些时候,服务器返回的JSON数据中的字段名采用“蛇形”命名法,如果要转成iOS中“驼峰”命名法,就要手动对keys做一次匹配。
Swift4.0中,只要指定Struct/Class中的CodingKeys并遵守CodingKeys协议枚举类型属性,并实现对应关系,就可以自动进行匹配替换解析。但注意如果CodingKeys中case没有匹配到JSON中的字段,解析就会失败。
从这一点来说,还是挺麻烦的,不如用Class中的setValue(_ value: Any?, forUndefinedKey key: String),然后匹配指定对应的属性名称。
struct GroceryProduct: Decodable{
var name: String
var points: Int
var description: String
// CodingKeys
private enum CodingKeys: String, CodingKey{
case name = "product_name"
case points = "product_points"
case description //保持一致,但必须实现所有属性
}
}
func swift4JSONParser() {
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
let decoder = JSONDecoder()
guard let products = try? decoder.decode([GroceryProduct].self, from: data) else{
fatalError("`JSON Decode Failed`")
}
print(products)
}
Simple Nested JSON Data
Swift4.0中对于JSON数据的嵌套结构解析,也有了新的方式,不过还是较为简单。
JSON数据:
[
{
"name": "Home Town Market",
"products": [
{
"name": "Banana",
"points": 200,
"description": "A banana that's perfectly ripe."
},
{
"name": "Apple",
"points": 200,
"description": "A banana that's perfectly ripe."
}
]
}
]
定义结构体
struct Product: Decodable {
var name: String
var points: Int
var description: String
}
struct GroceryStore: Decodable {
var name: String
var products: [Product]
}
嵌套解析
func swift4JSONParser() {
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
// 会自动匹配解析成相应的Product对象,因为Product也实现了Decodable协议
let decoder = JSONDecoder()
guard let stores = try? decoder.decode([GroceryStore].self, from: data) else{
fatalError("`JSON Decode Failed`")
}
print(stores)
Multiple Level Nested JSON Data
多层嵌套数据解析时,有一些结构是我们不需要存储的,这就我们定义一个中间的service模型来临时搭建这个结构。
JSON数据:
[
{
"name": "Big City Market",
"aisles": [
{
"name": "Sale Aisle",
"shelves": [
{
"name": "Seasonal Sale",
"product": {
"name": "Chestnuts",
"points": 700,
"description": "Chestnuts that were roasted over an open fire."
}
},
{
"name": "Last Season's Clearance",
"product": {
"name": "Pumpkin Seeds",
"points": 400,
"description": "Seeds harvested from a pumpkin."
}
}
]
}
]
}
]
定义存储数据模型
struct Product: Decodable {
var name: String
var points: Int
var description: String
}
struct GroceryStore {
var name: String
var products: [Product]
}
// 中间`架构`类型
struct GroceryStoreService: Decodable {
let name: String
let aisles: [Aisle]
struct Aisle: Decodable {
let name: String
let shelves: [Shelf]
struct Shelf: Decodable {
let name: String
let product: Product
}
}
}
// 扩展接口,实现数据解析
extension GroceryStore {
init(from service: GroceryStoreService) {
name = service.name
products = []
for aisle in service.aisles {
for shelf in aisle.shelves{
products.append(shelf.product)
}
}
}
}
嵌套解析
func swift4JSONParser() {
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
let decoder = JSONDecoder()
guard let serviceStores = try? decoder.decode([GroceryStoreService].self, from: data)else{
fatalError("`JSON Decode Failed`")
}
// 数据剥离存储
let stores = serviceStores.map{ GroceryStore(from: $0) }
print(stores)
}
Merge Data from Different Depths
合并不同深度层的数据。此时一般要转换成KeyedDecodingContainer进行解析。
JSON数据:
{
"Banana": {
"points": 200,
"description": "A banana grown in Ecuador."
},
"Orange": {
"points": 100,
"description": "A juicy orange."
}
}
数据模型:
struct GroceryStore {
struct Product {
let name: String
let points: Int
let description: String
}
var products: [Product]
init(products: [Product] = []) {
self.products = products
}
}
合并解析
// 扩展增加ProductKey实现CodingKey,便于深层次解析属性
extension GroceryStore {
struct ProductKey: CodingKey {
// 实现协议方法和属性
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
// 自定义keys
static let points = ProductKey(stringValue: "points")!
static let description = ProductKey(stringValue: "description")!
}
}
// 扩展实现Decodable协议,并通过decoder.container找到key对应的容器对象
extension GroceryStore: Decodable{
init(from decoder: Decoder) throws {
var products = [Product]()
// 找到包含ProductKey中的属性的所有容器
let container = try decoder.container(keyedBy: ProductKey.self)
// 然后遍历这个容器中的所有key,解析出容器中key对应的数据值
for key in container.allKeys {
let productContainer = try container.nestedContainer(keyedBy: ProductKey.self, forKey: key)
let points = try productContainer.decode(Int.self, forKey: .points)
let description = try productContainer.decode(String.self, forKey: .description)
let product = Product(name: key.stringValue, points: points, description: description)
products.append(product)
}
self.init(products: products)
}
}
func swift4JSONParser() {
// 数据获取
guard let fileURL = Bundle.main.url(forResource: "test.json", withExtension: nil),
let data = try? Data.init(contentsOf: fileURL) else{
fatalError("`JSON File Fetch Failed`")
}
// 数据解析
let decoder = JSONDecoder()
guard let store = try? decoder.decode(GroceryStore.self, from: data)else{
fatalError("`JSON Decode Failed`")
}
print(store.products)
}
JSONEncoder
要实现包含Struct/Class对象的JSON对象的编码,在Swift4.0中较为简单,只需要遵守Encodable协议,并指定要编码的keys和实现协议encode方法即可。
JSON数据:
[
{
"name": "Vegetables Store",
"products": [
{
"name": "Banana",
"points": 200,
"description": "A banana grown in Ecuador."
},
{
"name": "Orange",
"points": 100,
"description": "A juicy orange."
}
]
}
]
编码实现
struct Product: Decodable {
let name: String
let points: Int
let description: String
}
struct GroceryStore: Decodable {
var name: String
var products: [Product]
}
// 实现编码协议
extension GroceryStore: Encodable{
private enum CodingKeys: CodingKey{
case name
case products
}
// 封装要编码的数据结构
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(products, forKey: .products)
}
}
extension Product: Encodable{
private enum CodingKeys: CodingKey{
case name
case points
case description
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(points, forKey: .points)
try container.encode(description, forKey: .description)
}
}
// 要求object为实现了Encodable协议的对象
func swift4JSONEncode (withJSONObject object: T){
// 编码
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
guard let encodedData = try? encoder.encode(object),
let jsonText = String(data: encodedData, encoding: .utf8) else {
fatalError("`JSON Encode Failed`")
}
print(jsonText)
}
补充2017-09-22
在有些情况下,需要struct对象中的某些属性不是全部需要被存储和解析,就需要手动进行decode了
定义结构体
struct GitHubUser {
var id: Int
var type: String
var loginName: String
var avatarUrl: String
var homepageUrl: String
var profileUrl: String
var name: String
var company: String
var location: String
var blog: String
var bio: String
enum CodingKeys: String, CodingKey{
case id,type,name,company,location,blog,bio
case loginName = "login"
case avatarUrl = "avatar_url"
case homepageUrl = "html_url"
case profileUrl = "url"
}
}
自定义解析
extension GitHubUser: Decodable{
// 必须实现所有属性 - 初始值
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
type = try values.decode(String.self, forKey: .type)
loginName = try values.decode(String.self, forKey: .loginName)
avatarUrl = try values.decode(String.self, forKey: .avatarUrl)
homepageUrl = try values.decode(String.self, forKey: .homepageUrl)
profileUrl = try values.decode(String.self, forKey: .profileUrl)
// 以下属性为可选解析的,设置默认值
do {
name = try values.decode(String.self, forKey: .name)
}catch{
name = ""
}
do {
company = try values.decode(String.self, forKey: .company)
}catch{
company = ""
}
do {
location = try values.decode(String.self, forKey: .location)
}catch{
location = ""
}
do{
blog = try values.decode(String.self, forKey: .blog)
}catch{
blog = ""
}
do{
bio = try values.decode(String.self, forKey: .bio)
}catch{
bio = ""
}
}
}
如果对你有帮助,别忘了点个❤️并关注下我哦。
swift php json解析,Swift 4.0 | JSON数据的解析和编码相关推荐
- 深入解析android 5.0系统 pdf,深入解析Android 5.0系统 刘超 中文pdf_源雷技术空间
资源名称:深入解析Android 5.0系统 刘超 中文pdf <深入解析Android 5.0系统>详细剖析了最新Android 5.0 系统主要框架的原理和具体实现.本书共24章,覆盖 ...
- 爬数据html解析,jsoup网络爬取数据HTML解析
Jsoup是一款网络爬取数据的解析器,可以解析HTML文件中的任何子节点,支持离线HTML文件.字符型HTML内容.URL的解析.非常方便和实用. Document doc = Jsoup.conne ...
- js解析json js获取json里面的某个节点的数据 js解析json数据
获取data里面的节点 $.ajax({type: "GET",url: '../api/数据接口.aspx',dataType: 'json',success: function ...
- mybatis 解析Integer为0的属性,解析成空字符串
使用Mybatis时,常常会判断属性是否为空 1 <if test="type != null and type != ''"> 2 and type = #{type ...
- 【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )
文章目录 安卓直播推流专栏博客总结 一. AAC 音频格式解析 二. FLV 音频数据标签解析 1. 分析 FLV 格式中的 AAC 音频格式数据 2. AAC 音频特殊配置 3. AAC 音频数据标 ...
- python网络数据包分析_Pyshark:使用了WirdShark的Python数据包解析工具(Tshark)
Pyshark Pyshark是一款针对tshark的Python封装器,在Pyshark的帮助下,广大研究人员可以使用wireshark的解析器来进行Python数据包解析.扩展文档:[Pyshar ...
- swift:使用NSJSONSerialization和SwiftyJSON两种方法解析网络返回的json格式数据
在我的博客(下面)两个实验的基础上,使用NSJSONSerialization和SwiftyJSON两种方法解析网络返回的json格式数据,参照视频实现的"天气信息"小实验 1 创 ...
- Swift 网络请求数据与解析
一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...
- Swift 掌控Moya的网络请求、数据解析与缓存
Moya 在Swift开发中起着重要的网络交互作用,但是还有不如之处,比如网络不可用时,返回的 Response 为 nil,这时还得去解析相应的 Error Codable 可以帮助我们快速的解析数 ...
最新文章
- python gui label_更新tkinter Label以在pythongui上显示一个文本文件,每次一行
- STL标准库六大组件
- NLP:自然语言处理技术近十年发展技术更迭的简介、案例之详细攻略(持续更新)
- 【QA】pytorch中的worker如何工作的
- thinkphp5 图片压缩旋转_【好工具】在线免费无限制的PDF处理工具(转换、编辑、分割、合并、压缩)...
- ArcGIS实验教程——实验七:矢量数据空间校正(Spatial Adjustment)
- 数据结构与算法——二叉树、堆、优先队列
- AsyncTask理解- Day36or37
- python flask框架是什么_Python三大web框架分别是什么 哪个更好
- 2018 我所了解的 Vue 知识大全(一)
- Linux ---- 安装虚拟机
- 抑郁症自测量表测试软件,抑郁症测试专业量表(SDS)
- 中科大2021计算机考研分数线,中国科学技术大学2021年考研复试各科分数线_中国科大考多少分能进复试-聚创中国科大考研网...
- 三维激光扫描仪点云数据处理与建模
- 计算机 窗口打开的方法,如何打开命令行窗口?两种实用的方法介绍
- 狂奔的“智慧校园”:监控学生的生意,会持续吗?
- c++栈实现简单计算器
- 这所C9高校的8个CS院系,有你心仪的吗?
- feign.codec.DecodeException异常解决方案
- php 转换 html code,PHP 将 HTML 代码 转换到 UBB 论坛代码
热门文章
- 逃避型人格分析,如何改变逃避型性格?
- postgres vacuum full 和 vacuum
- 全球与中国磨料水射流切割机市场深度研究分析报告
- 「镁客早报」FF称恒大健康公告内容“部分不属实”;特斯拉实现汽车日产7000辆目标...
- 公司不会告诉你的潜规则
- VMware虚拟机安装CentOS7,登录时,输入密码出现抱歉,没有奏效,请再试一遍。解决方法:
- 数字图像处理中的一些概念
- SQL面试题:经典排名问题(相同分数是否要并列,排名是否有间隔)
- MySQL基础之查询语句
- web前端第二周学习