Json的解码与编码操作,这里使用swift自带的类JSONDecoderJSONEncoder

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 4 JSON 解析指南

Swift 4 JSON 解析进阶

JSONDecoder的使用

Swift Json解析与model互转的更多相关文章

  1. Swift Json 解析错误

    昨天在开发公司的ios程序时,遇见一个json解析的问题,并且是一个非常奇怪的问题. 因为原来的代码比较复杂,所以对代码进行了一些简化,具体代码如下: 服务器返回格式(PHP): array( arr ...

  2. Swift Json解析基础

    func JSONToData(obj:Any) -> Data { //先判断是否可以转换 if !JSONSerialization.isValidJSONObject(obj) { ret ...

  3. Json转model对象,model转json,解析json字符串

    GitHub链接: https://github.com/mozhenhau/D3Json D3Json 通过swift的反射特性,把json数据转换为model对象,本类最主要是解决了其他一般jso ...

  4. iOS开发之Swift 4 JSON 解析指南

    Apple 终于在 Swift 4 的 Foundation 的模块中添加了对 JSON 解析的原生支持. 虽然已经有很多第三方类库实现了 JSON 解析,但是能够看到这样一个功能强大.易于使用的官方 ...

  5. java中常见的json解析方法、库以及性能对比

    常见的json解析有原生的JSONObject和JSONArray方法,谷歌的GSON库,阿里的fastjson,还有jackson,json-lib. Gson(项目地址:https://githu ...

  6. Android okHttp网络请求之Json解析

    前言: 前面两篇文章介绍了基于okHttp的post.get请求,以及文件的上传下载,今天主要介绍一下如何和Json解析一起使用?如何才能提高开发效率? okHttp相关文章地址: Android o ...

  7. ios的网络数据下载和json解析

    ios的网络数据下载和json解析 简介 在本文中,笔者将要给大家介绍如何使用nsurlconnection 从网上下载数据,以及解析json数据格式,以及如何显示数据和图片的异步下载显示. 涉及的知 ...

  8. 一起写一个JSON解析器

    [本篇博文会介绍JSON解析的原理与实现,并一步一步写出来一个简单但实用的JSON解析器,项目地址:SimpleJSON.希望通过这篇博文,能让我们以后与JSON打交道时更加得心应手.由于个人水平有限 ...

  9. UI学习笔记---第十六天XML JSON解析

    一.解析的基本概念 从事先规定好的格式中提取数据 解析的前提:提前约定好格式.数据提供方按照格式提供数据,数据方按照格式获取数据 常见解析方式XML解析JSON解析 二.XML:可扩展标记语言 XML ...

随机推荐

  1. iOS收起键盘

    在UIViewController中收起键盘,有四种代码方式: 1.让相应的控件放弃第一响应者 /** 放弃第一响应者 */ [self.nameTextField resignFirstRespon ...

  2. Lesson 13 The search for oil

    What do oilmen want to achieve as soon as they strike oil? The deepest holes of all are made for oil ...

  3. vue iviem UI grid布局

    Grid 栅格 概述 我们采用了24栅格系统,将区域进行24等分,这样可以轻松应对大部分布局问题.使用栅格系统进行网页布局,可以使页面排版美观.舒适. 我们定义了两个概念,行row和列col,具体使用 ...

  4. Android开发_*.R文件无法自动生成

    问题描述:             今天是我决定专注Android开发的第一天,我在网上下载了一个数独游戏的源码,准备开始着手学习.在导入之后出现Java文件中import *.R文件报错,在gen目 ...

  5. WebGL绘制正方体

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. 虚拟机字节码指令表 JVM

    虚拟机字节码指令表 标签(空格分隔): Java基础 JVM 记录虚拟机字节码指令,方便分析.以下内容来自<深入理解Java虚拟机> 字节码 助记符 指令含义 0x00 nop 什么都不做 ...

  7. centos7下yourcompleteme安装

    以前装过一回,没成功,现在再来一次 yourcompleteme git https://github.com/ycm-core/YouCompleteMe#installation 检查软件版本 v ...

  8. python学习0day

    一开始学习python没有什么感觉,也没怎么用到,时间间隔大概有一年了开始重新拾起python,话说滋味不太好受,推荐大家学到就常常的练习,不要和小白一样,难受.... 推荐一个网站: 菜鸟教程 - ...

  9. 如何使用linux查看tomcat日志

  10. 说说mysql和oracle他门的分页查询,分别是怎么实现的?

    MySQL: 1.MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了. 2.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两 ...