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. Ubuntu16.04深度学习基本环境搭建,tensorflow , keras , pytorch , cuda

    Ubuntu16.04深度学习基本环境搭建,tensorflow , keras , pytorch , cuda Ubuntu16.04安装 参考https://blog.csdn.net/flyy ...

  2. python之字符串,列表,字典,元组,集合内置方法总结

    目录 数字类型的内置方法 整型/浮点型 字符串类型的内置方法 列表的内置方法 字典的内置方法 元组的内置方法 集合类型内置方法 布尔类型 数据类型总结 数字类型的内置方法 整型/浮点型 加 + 减 - ...

  3. redis队列与RabbitMQ队列区别

    消息队列(Message Queue)是一种应用间的通信方式,消息发送后可以立即返回,由消息系统来确保消息的可靠传递.消息发布者只管把消息发布到 MQ 中而不用管谁来取,消息使用者只管从 MQ 中取消 ...

  4. 【转载】如何快速转载CSDN中的博客

    前言   对于喜欢逛CSDN的人来说,看别人的博客确实能够对自己有不小的提高,有时候看到特别好的博客想转载下载,但是不能一个字一个字的敲了,这时候我们就想快速转载别人的博客,把别人的博客移到自己的空间 ...

  5. 动态、指针field-symbols初探

    DATA: BEGIN OF STRUC, COMP1 VALUE ', COMP2 VALUE ', COMP3 TYPE STRING VALUE 'bruce king', END OF STR ...

  6. JavaScript 标识符,关键字和保留字

    JavaScript 标识符,关键字和保留字 标识符 标识符(Identifier)就是名称的专业术语.JavaScript 标识符包括变量名.函数名.参数名和属性名. 合法的标识符应该注意以下强制规 ...

  7. 深入了解memcached

    一.memcached如何支持高并发 Memcached使用多路复用 I/O模型(如epoll.select等).传统阻塞 I/O中,系统可能会因为某个用户连接还没做好 I/O准备而一直等待,直到这个 ...

  8. 第1课 VMware的NSX全面落地软件定义网络SDN

    SDN的定义: 即软件定义网络(Software Defined Network)的缩写,它是一种基于网络架构的创新,一种在已存在物理传输网络之上的抽象形态,它是一种体系结构,它是众多网络虚拟化技术中 ...

  9. Android的事件处理机制之基于回调的事件处理

    回调机制 如果说事件监听机制是一种委托式的事件处理,那么回调机制则与之相反,对于基于回调的事件处理模型来说,事件源与事件监听器是统一的,换种方法说事件监听器完全消失了,当用户在GUI组件上激发某个事件 ...

  10. 一个小证明(题解 P5425 Part1)

    所以这道题为什么可以这样做 嗯,我也不知道,不过我是来填坑的. \(Q\):为什么要把牛分成\(1\),\(1\)......\(N-K+1\)这样的\(K\)组呢? \(A\):我们设第\(i\)组 ...