Swift Json解析与model互转
Json的解码与编码操作,这里使用swift自带的类JSONDecoder 和 JSONEncoder
1、基础处理
如果你的 JSON 数据结构和你使用的 Model 对象结构一致的话,那么解析过程将会非常简单
2、自定义键值名
默认情形下 Keys 是由编译器自动生成的枚举类型。该枚举遵守 CodingKey 协议并建立了属性和编码后格式之间的关系
struct Beer : Codable {
// ...
private enum CodingKeys : String, CodingKey {
case name
case abv = "alcohol_by_volume"
case brewery = "brewery_name"
case style
}
}
3、时间格式处理
4、浮点类型处理
5、Data 处理
有时候服务端 API 返回的数据是 base64 编码过的字符串。
对此,我们可以在 JSONEncoder 使用以下策略:
- .base64
- .custom( (Data, Encoder) throws -> Void)
反之,编码时可以使用:
- .base64
- .custom( (Decoder) throws -> Data)
显然,.base64 时最常见的选项,但如果需要自定义的话可以采用 block 方式。
6、Wrapper Keys
通常 API 会对数据进行封装,这样顶级的 JSON 实体 始终是一个对象。
例如:
{
"beers": [ {...} ]
}
在 Swift 中我们可以进行对应处理:
struct BeerList : Codable {
let beers: [Beer]
}
因为键值与属性名一致,所有上面代码已经足够了。
7、Root Level Arrays
如果 API 作为根元素返回数组,对应解析如下所示:
let decoder = JSONDecoder()
let beers = try decoder.decode([Beer].self, from: data)
需要注意的是,我们在这里使用Array作为类型。只要 T 可解码,Array <T> 就可解码。
8、Dealing with Object Wrapping Keys
另一个常见的场景是,返回的数组对象里的每一个元素都被包装为字典类型对象。
[
{
"beer" : {
"id": "uuid12459078214",
"name": "Endeavor",
"abv": 8.9,
"brewery": "Saint Arnold",
"style": "ipa"
}
}
]
你可以使用上面的方法来捕获此 Key 值,但最简单的方式就是认识到该结构的可编码的实现形式。
如下:
[[String:Beer]]
或者更易于阅读的形式:
Array<Dictionary<String, Beer>>
与上面的 Array<T> 类似,如果 K 和 T 是可解码 Dictionary<K,T> 就能解码。
let decoder = JSONDecoder()
let beers = try decoder.decode([[String:Beer]].self, from: data)
dump(beers)
1 element
▿ 1 key/value pair
▿ (2 elements)
- key: "beer"
▿ value: __lldb_expr_37.Beer
- name: "Endeavor"
- brewery: "Saint Arnold"
- abv: 8.89999962
- style: __lldb_expr_37.BeerStyle.ipa
9、更复杂的嵌套
有时候 API 的响应数据并不是那么简单。顶层元素不一定只是一个对象,而且通常情况下是多个字典结构。
例如:
{
"meta": {
"page": 1,
"total_pages": 4,
"per_page": 10,
"total_records": 38
},
"breweries": [
{
"id": 1234,
"name": "Saint Arnold"
},
{
"id": 52892,
"name": "Buffalo Bayou"
}
]
}
在 Swift 中我们可以进行对应的嵌套定义处理:
struct PagedBreweries : Codable {
struct Meta : Codable {
let page: Int
let totalPages: Int
let perPage: Int
let totalRecords: Int
enum CodingKeys : String, CodingKey {
case page
case totalPages = "total_pages"
case perPage = "per_page"
case totalRecords = "total_records"
}
}
struct Brewery : Codable {
let id: Int
let name: String
}
let meta: Meta
let breweries: [Brewery]
}
该方法的最大优点就是对同一类型的对象做出不同的响应(可能在这种情况下,“brewery” 列表响应中只需要 id 和 name 属性,但是如果查看详细内容的话则需要更多属性内容)。因为该情形下 Brewery 类型是嵌套的,我们依旧可以在其他地方进行不同的 Brewery 类型实现。
10、无法覆盖继承的问题,自定义解码和编码
============================================代码示例============================================
model代码如下
//为了将 JSON 字符串转化为 Beer 类型的实例,我们需要将 Beer 类型标记为 Codable。 enum BeerStyle: String, Codable {
case ipa
case stout
case kolsch
} struct Beer: Codable {
let name: String
let abv: Float
let brewery: String
let style: BeerStyle
} /// json 的key与结构体属性不是对应的,需要自定义键值名
struct Beer2: Codable {
let name: String
let abv: Float
let brewery: String
let style: BeerStyle private enum CodingKeys: String, CodingKey {
case name
case abv = "alcohol_by_volume"
case brewery = "brewery_name"
case style
} init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: Beer2.CodingKeys.name)
abv = try values.decode(Float.self, forKey: Beer2.CodingKeys.abv)
brewery = try values.decode(String.self, forKey: Beer2.CodingKeys.brewery)
style = try values.decode(BeerStyle.self, forKey: Beer2.CodingKeys.style)
}
} class Wine: NSObject, Codable {
var abv: Float?
} class Beer3: Wine {
var name: String?
var brewery: String?
var style: BeerStyle?
} class Beer4: Wine {
var name: String?
var brewery: String?
var style: BeerStyle? private enum CodingKeys: String, CodingKey {
case name
case abv = "alcohol_by_volume"
case brewery = "brewery_name"
case style
} /// 自定义编码
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(name, forKey: .name)
try container.encode(abv, forKey: .abv)
try container.encode(brewery, forKey: .brewery)
try container.encode(style, forKey: .style)
} /// 自定义解码
required init(from decoder: Decoder) throws {
super.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: Beer4.CodingKeys.name)
abv = try values.decode(Float.self, forKey: Beer4.CodingKeys.abv)
brewery = try values.decode(String.self, forKey: Beer4.CodingKeys.brewery)
style = try values.decode(BeerStyle.self, forKey: Beer4.CodingKeys.style)
}
} /// 时间格式处理
struct Foo: Encodable {
let date: Date
} /// 浮点类型处理
struct Numbers: Decodable {
let a: Float
let b: Float
let c: Float
}
编码与解码
import UIKit class jsonViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad() jsonFunc1()
jsonFunc2()
jsonFunc3()
jsonFunc4()
jsonFunc7()
jsonFunc8()
}
} // MARK: https://segmentfault.com/a/1190000009929819#articleHeader9 extension jsonViewController {
/// JSON 数据结构和Model 对象结构一致
func jsonFunc1() {
let jsonString: String = "{\"name\":\"Endeavor\",\"abv\":8.9,\"brewery\":\"Saint Arnold\",\"style\":\"ipa\"}"
let jsonData = jsonString.data(using: String.Encoding.utf8)
let decoder = JSONDecoder()
// JSON 数据解析为 Beer 实例对象
let beer = try! decoder.decode(Beer.self, from: jsonData!) print("name=\(beer.name),abv=\(beer.abv),brewery=\(beer.brewery),style=\(beer.style)")
/* 打印
name=Endeavor,abv=8.9,brewery=Saint Arnold,style=ipa
*/ let encoder = JSONEncoder()
// 默认 outputFormatting 属性值为 .compact,输出效果如上。如果将其改为 .prettyPrinted 后就能获得更好的阅读体检
encoder.outputFormatting = .prettyPrinted
// 将 Beer 实例转化为 JSON
let jsonData2 = try! encoder.encode(beer) print(String(bytes: jsonData2, encoding: String.Encoding.utf8) ?? "") /* 打印
{
"style" : "ipa",
"brewery" : "Saint Arnold",
"name" : "Endeavor",
"abv" : 8.8999996185302734
}
*/
} /// JSON 数据结构和Model 对象结构不一致,自定义键值名
func jsonFunc2() {
let jsonString: String = "{\"name\":\"Endeavor\",\"alcohol_by_volume\":8.9,\"brewery_name\":\"Saint Arnold\",\"style\":\"ipa\"}"
let jsonData = jsonString.data(using: String.Encoding.utf8)
let decoder = JSONDecoder()
// JSON 数据解析为 Beer 实例对象
let beer = try! decoder.decode(Beer2.self, from: jsonData!) print("name=\(beer.name),abv=\(beer.abv),brewery=\(beer.brewery),style=\(beer.style)")
/* 打印
name=Endeavor,abv=8.9,brewery=Saint Arnold,style=ipa
*/ let encoder = JSONEncoder()
// 默认 outputFormatting 属性值为 .compact,输出效果如上。如果将其改为 .prettyPrinted 后就能获得更好的阅读体检
encoder.outputFormatting = .prettyPrinted
// 将 Beer 实例转化为 JSON
let jsonData2 = try! encoder.encode(beer) print(String(bytes: jsonData2, encoding: String.Encoding.utf8) ?? "")
/* 打印
{
"style" : "ipa",
"name" : "Endeavor",
"brewery_name" : "Saint Arnold",
"alcohol_by_volume" : 8.8999996185302734
}
*/
} /// Codable 默认实现无法覆盖继承这种情况
func jsonFunc3() {
let jsonDic: [String: Any] = ["name": "Endeavor", "abv": 8.9, "brewery": "Saint Arnold", "style": "ipa"]
let jsonData = JSONToData(obj: jsonDic) let decoder = JSONDecoder()
// JSON 数据解析为 Beer 实例对象
let beer = try! decoder.decode(Beer3.self, from: jsonData) print("name=\(String(describing: beer.name)),abv=\(String(describing: beer.abv)),brewery=\(String(describing: beer.brewery)),style=\(String(describing: beer.style))")
/* 打印
name=nil,abv=Optional(8.9),brewery=nil,style=nil
*/ //解析成功但是 name、brewery、style 三个属性全部为 nil,显然,这不是我们想要的结果。Codable 默认实现无法覆盖继承这种情况
} /// 解决 无法覆盖继承的问题,自定义解码和编码
func jsonFunc4() {
let jsonString: String = "{\"name\":\"Endeavor\",\"alcohol_by_volume\":8.9,\"brewery_name\":\"Saint Arnold\",\"style\":\"ipa\"}"
let jsonData = jsonString.data(using: String.Encoding.utf8)
let decoder = JSONDecoder()
// JSON 数据解析为 Beer 实例对象
let beer = try! decoder.decode(Beer4.self, from: jsonData!) print("name=\(String(describing: beer.name)),abv=\(String(describing: beer.abv)),brewery=\(String(describing: beer.brewery)),style=\(String(describing: beer.style))")
/* 打印
name=Optional("Endeavor"),abv=Optional(8.9),brewery=Optional("Saint Arnold"),style=Optional(abcProject.BeerStyle.ipa)
*/ let encoder = JSONEncoder()
// 默认 outputFormatting 属性值为 .compact,输出效果如上。如果将其改为 .prettyPrinted 后就能获得更好的阅读体检
encoder.outputFormatting = .prettyPrinted
// 将 Beer 实例转化为 JSON
let jsonData2 = try! encoder.encode(beer) print(String(bytes: jsonData2, encoding: String.Encoding.utf8) ?? "")
/* 打印
{
"style" : "ipa",
"name" : "Endeavor",
"brewery_name" : "Saint Arnold",
"alcohol_by_volume" : 8.8999996185302734
}
*/
} /// 时间格式处理
func jsonFunc7() {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let foo = Foo(date: Date())
let jsonData2 = try! encoder.encode(foo) print(String(bytes: jsonData2, encoding: String.Encoding.utf8) ?? "") /*
其他日期编码格式选择如下: .formatted(DateFormatter) - 当你的日期字符串是非标准格式时使用。需要提供你自己的日期格式化器实例。
.custom( (Date, Encoder) throws -> Void ) - 当你需要真正意义上的自定义时,使用一个闭包进行实现。
.millisecondsSince1970、 .secondsSince1970 - 这在 API 设计中不是很常见。 由于时区信息完全不在编码表示中,所以不建议使用这样的格式,这使得人们更容易做出错误的假设。
对日期进行 Decoding 时基本上是相同的选项,但是 .custom 形式是 .custom( (Decoder) throws -> Date ),所以我们给了一个解码器并将任意类型转换为日期格式。 */
} /// 浮点类型处理
func jsonFunc8() {
let jsonString: String = "{\"a\": \"NaN\", \"b\": \"+Infinity\", \"c\": \"-Infinity\" }"
let jsonData = jsonString.data(using: String.Encoding.utf8)
let decoder = JSONDecoder() decoder.nonConformingFloatDecodingStrategy =
.convertFromString(
positiveInfinity: "+Infinity",
negativeInfinity: "-Infinity",
nan: "NaN") let numbers = try! decoder.decode(Numbers.self, from: jsonData!)
dump(numbers) /* 打印 ▿ abcProject.Numbers
- a: nan
- b: inf
- c: -inf */
}
}
参考链接
Swift Json解析与model互转的更多相关文章
- Swift Json 解析错误
昨天在开发公司的ios程序时,遇见一个json解析的问题,并且是一个非常奇怪的问题. 因为原来的代码比较复杂,所以对代码进行了一些简化,具体代码如下: 服务器返回格式(PHP): array( arr ...
- Swift Json解析基础
func JSONToData(obj:Any) -> Data { //先判断是否可以转换 if !JSONSerialization.isValidJSONObject(obj) { ret ...
- Json转model对象,model转json,解析json字符串
GitHub链接: https://github.com/mozhenhau/D3Json D3Json 通过swift的反射特性,把json数据转换为model对象,本类最主要是解决了其他一般jso ...
- iOS开发之Swift 4 JSON 解析指南
Apple 终于在 Swift 4 的 Foundation 的模块中添加了对 JSON 解析的原生支持. 虽然已经有很多第三方类库实现了 JSON 解析,但是能够看到这样一个功能强大.易于使用的官方 ...
- java中常见的json解析方法、库以及性能对比
常见的json解析有原生的JSONObject和JSONArray方法,谷歌的GSON库,阿里的fastjson,还有jackson,json-lib. Gson(项目地址:https://githu ...
- Android okHttp网络请求之Json解析
前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...
- ios的网络数据下载和json解析
ios的网络数据下载和json解析 简介 在本文中,笔者将要给大家介绍如何使用nsurlconnection 从网上下载数据,以及解析json数据格式,以及如何显示数据和图片的异步下载显示. 涉及的知 ...
- 一起写一个JSON解析器
[本篇博文会介绍JSON解析的原理与实现,并一步一步写出来一个简单但实用的JSON解析器,项目地址:SimpleJSON.希望通过这篇博文,能让我们以后与JSON打交道时更加得心应手.由于个人水平有限 ...
- UI学习笔记---第十六天XML JSON解析
一.解析的基本概念 从事先规定好的格式中提取数据 解析的前提:提前约定好格式.数据提供方按照格式提供数据,数据方按照格式获取数据 常见解析方式XML解析JSON解析 二.XML:可扩展标记语言 XML ...
随机推荐
- java编程,通过代理服务器访问外网的FTP
有些时候我们的网络不能直接连接到外网, 需要使用http或是https或是socket代理来连接到外网, 这里是java使用代理连接到外网的一些方法, 希望对你的程序有用.方法一:使用系统属性来完成代 ...
- Kali环境使用Metasploit生成木马入侵安卓手机
Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风险情报.这些功能包括智能开发,代码审计, ...
- HTTP协议调试工具汇总
前言 本文收集了大量抓包工具,近40款,涵盖了各种开发语言(Java,C#,Delphi,C,C++,Objective-C,Node.js,Go,Python).各类前端(GUI,TUI,CUI,W ...
- MySQL部分语法
MySQL 1.先进入到d盘根目录(自己安装MySQL的盘符) cd d:\2.输入net start mysql3.在d盘的根目录下输入mysql -u root -p就可以进入mysql的命令提示 ...
- 字符串替换 (replace)
将文本文件中指定的字符串替换成新字符串. 由于目前的OJ系统暂时不能支持用户读入文件,我们编写程序从键盘输入文件中的内容,当输入的一行为end时,表示结束.end后面有两个字符串,要求用第二个字符串替 ...
- P3381 【模板】最小费用最大流(spfa板子)
#include<bits/stdc++.h> using namespace std; #define lowbit(x) ((x)&(-x)) typedef long lon ...
- Java核心API需要掌握的程度
分类: java技术2009-08-29 01:03 213人阅读 评论(0) 收藏 举报 javaapiswingxmlio Java的核心API是非常庞大的,这给开发者来说带来了很大的方便,经常人 ...
- Java安全中的“大坑”,跨平台真“浮云”
Java安全HttpDB 最近在做一个开源项目HttpDB,它的目标是在互联网中通过JDBC安全的查询数据库,解决云计算报表的数据库访问问题. 数据传输使用AES加密算法,用到了Java提供的安全库j ...
- Python集合语法
a = {1,2,3,4,5,6,7,8,"aa","2"} b = {2,3,4,5,6,7,8,9,99,7,6,6} v = a.intersect ...
- 连接数据库 - (mysql-thinkphp) (2)
1.现在conf里面写好选择的数据库 选择好了以后 2.在index里面输入 查询mysql数据库里面的表tables_priv的所有数据 public function index() { $res ...