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 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. NoSQL 是什么

    NoSQL 全称 Not only SQL ,是一种相对较新的数据库设计方式,传统的关系型数据库使用的是固定模式,并将数据分割在多个表中,然而,对于大数据集的情况,数据量太大使其难以存放在单一的服务器 ...

  2. C语言结构体指针(指向结构体的指针)详解

    C语言结构体指针详解 一.前言 一个指向结构体的变量的指针表示的是这个结构体变量占内存中的起始位置,同样它也可以指向结构体变量数组. *a).b 等价于 a->b. "."一 ...

  3. java基础多线程

    线程的创建 方式1:继承Java.lang.Thread类,并覆盖run() 方法 package com.demo.Thread; public class ThreadDemo01 extends ...

  4. Linux用户和用户组管理命令

    一.用户管理命令 1.useradd   创建用户或更新默认新用户的信息 使用方法  useradd [options] 用户名 选项: useradd -u           指定UID具体数值, ...

  5. 九:File类,文件的操作

    File的常用方法:

  6. L2-012. 关于堆的判断(最小堆)

    将一系列给定数字顺序插入一个初始为空的小顶堆H[].随后判断一系列相关命题是否为真.命题分下列几种: “x is the root”:x是根结点: “x and y are siblings”:x和y ...

  7. iOS Framework制作流程

    1.新建工程选择iOS —> Cocoa Touch Framework 2.进入创建好的工程删除掉自带的工程同名头文件 3.添加所需文件 4.TARGETS —> Build Setti ...

  8. Java提升三:函数式接口

    1. 定义 函数式接口即是有且仅有一个抽象方法的接口. 注意: (1)函数式接口只对于抽象方法有要求,对于接口中的默认方法,静态方法,私有方法数量并不作特殊要求. (2)既然函数式接口定义了抽象方法, ...

  9. mac搭建nginx

    0.介绍 Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务.Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler ...

  10. 传入sql语句,执行完提取内容赋值到控件上

    class procedure DBTools.FillStrings(ComboBoxEh: TDBComboBoxEh; sql: string; Default: Boolean = False ...