一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Swift - Swift Json實例詳細(xì)解析

Swift Json實例詳細(xì)解析

2021-01-10 15:51桃紅宿雨 Swift

這篇文章主要給大家介紹了關(guān)于Swift Json解析的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。

前言

客戶端開發(fā)項目中,不可避免地需要解析網(wǎng)絡(luò)數(shù)據(jù)---將服務(wù)端下發(fā)的JSON數(shù)據(jù)解析成客戶端可閱讀友好的Model。Objective-C下使用最多的是JSONModel,它能在OC Runtime基礎(chǔ)下很好地完成解析工作。那么在純Swift代碼中,這個功能是如何實現(xiàn)的?下面開始我們的探索~

  • 手動解析
  • 原生:Swift4.0 JSONDecoder
  • JSONDecoder 問題 及 解決方案

手動解析

假設(shè)一個User類要解析,Json如下:

?
1
2
3
4
5
{
 "userId": 1,
 "name": "Jack",
 "height": 1.7,
}

對應(yīng)的創(chuàng)建一個User結(jié)構(gòu)體(也可以是類):

?
1
2
3
4
5
struct User {
 var userId: Int?
 var name: String?
 var height: CGFloat?
}

把JSON轉(zhuǎn)成User

在Swift4.0前,我們以手動解析的方式將JSON model化。給User加一個以JSON為參數(shù)的初始化方法,代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
struct User {
 ...
 init?(json: [String: Any]) {
  guard let userId = json["userId"] as? Int,
  let name = json["name"] as? String,
  let height = json["height"] as? CGFloat else { return nil }
  self.userId = userId
  self.name = name
  self.height = height
 }
}

依次從json中取出model所需的具體類型的數(shù)據(jù),填充到具體對應(yīng)屬性中。如果其中一個轉(zhuǎn)換失敗或者沒有值,初始化會失敗返回nil。

如果某個值不需要強校驗,直接取值再賦值,把guard let內(nèi)的語句去掉。例如,若height不用校驗,可看如下代碼:

?
1
2
3
4
5
6
7
8
9
10
struct User {
 ...
 init?(json: [String: Any]) {
  guard let userId = json["userId"] as? Int,
  let name = json["name"] as? String else { return nil }
  self.userId = userId
  self.name = name
  self.height = json["height"] as? CGFloat
 }
}

原生:Swift4.0 JSONDecoder

2017年6月份左右Swift4.0發(fā)布,其中一個重大更新就是JSON的加解密。擺脫手工解析字段的繁瑣,聊聊幾行代碼就可將JSON轉(zhuǎn)換成Model。與Objective-C下的JSONModel極為相似。同樣解析上述例子中的User,Swift4.0可以這么寫:

?
1
2
3
4
5
6
7
8
9
10
struct User: Decodable {
 var userId: Int?
 var name: String?
 var height: CGFloat?
}
 
let decoder = JSONDecoder()
if let data = jsonString.data(using: String.Encoding.utf8) {
 let user = try? decoder.decode(User.self, from: data)
}

so easy~ 與手動解析不同點在于:

1.移除了手寫init?方法。不需要手動解了

2.User實現(xiàn)Decodable協(xié)議,協(xié)議的定義如下:

?
1
2
3
4
5
6
7
8
9
10
/// A type that can decode itself from an external representation.
public protocol Decodable {
 /// Creates a new instance by decoding from the given decoder.
 ///
 /// This initializer throws an error if reading from the decoder fails, or
 /// if the data read is corrupted or otherwise invalid.
 ///
 /// - Parameter decoder: The decoder to read data from.
 public init(from decoder: Decoder) throws
}

Decodable協(xié)議只有一個方法public init(from decoder: Decoder) throws---以Decoder實例進(jìn)行初始化,初始化失敗可能拋出異常。慶幸的是,只要繼承Decodable協(xié)議,系統(tǒng)會自動檢測類中的屬性進(jìn)行初始化工作,省去了人工解析的麻煩~

3.使用了JSONDecoder。它是真正的解析工具,主導(dǎo)整個解析過程

讀到這里,是不是覺得人生從黑暗邁向了光明~~

可是,它并不完美...

JSONDecoder問題及方案

解析JSON經(jīng)常遇到這樣兩種不一致問題:

  • 服務(wù)端下發(fā)的key跟端上不一致。比如,服務(wù)端下發(fā)key="order_id",端上定義key="orderId"
  • 服務(wù)端下發(fā)的日期表達(dá)是yyyy-MM-dd HH:mm或者時間戳,但端上是Date類型
  • 服務(wù)端下發(fā)的基本類型和端上定義的不一致。服務(wù)端下發(fā)的是String,端上定義的Int,等

前兩個問題JSONDecoder都能很好地解決。

第一個key不一致問題,JSONDecoder有現(xiàn)成的方案。以上面介紹的例子來說,假設(shè)服務(wù)端返回的key是user_id而不是userId,那么我們可以使用JSONDecoder的CodingKeys像JSONModel一樣對屬性名稱在加解密時的名稱做轉(zhuǎn)換。User修改如下:

?
1
2
3
4
5
6
7
8
9
10
struct User: Decodable {
 var userId: Int?
 var name: String?
 var height: CGFloat?
 enum CodingKeys: String, CodingKey {
  case userId = "user_id"
  case name
  case height
 }
}

第二個,Date轉(zhuǎn)換問題。JSONDecoder也為我們提供了單獨的API:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
open class JSONDecoder {
 /// The strategy to use for decoding `Date` values.
 public enum DateDecodingStrategy {
 
  /// Defer to `Date` for decoding. This is the default strategy.
  case deferredToDate
 
  /// Decode the `Date` as a UNIX timestamp from a JSON number.
  case secondsSince1970
 
  /// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
  case millisecondsSince1970
 
  /// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
  case iso8601
 
  /// Decode the `Date` as a string parsed by the given formatter.
  case formatted(DateFormatter)
 
  /// Decode the `Date` as a custom value decoded by the given closure.
  case custom((Decoder) throws -> Date)
 }
 ......
 /// The strategy to use in decoding dates. Defaults to `.deferredToDate`.
 open var dateDecodingStrategy: JSONDecoder.DateDecodingStrategy
}

設(shè)置好了JSONDecoder屬性dateDecodingStrategy后,解析Date類型就會按照指定的策略進(jìn)行解析。

類型不一致

至此,JSONDecoder為我們提供了

  • 解析不同key值對象
  • Date類型可自定義轉(zhuǎn)換
  • Float在一些正負(fù)無窮及無值得特殊表示。(出現(xiàn)的概率很少,不作具體說明了)

但遇到基本類型端上與服務(wù)端不一致時(比如一個數(shù)字1,端上的Code是Int型,服務(wù)端下發(fā)String:"1"),JSONDecoder會拋出typeMismatch異常而終結(jié)整個數(shù)據(jù)的解析。

這讓人有點懊惱,端上的應(yīng)用,我們希望它能夠盡可能穩(wěn)定,而不是某些情況下遇到若干個基本類型不一致整個解析就停止,甚至是 Crash。

如下面表格所示,我們希望類型不匹配時,能夠這么處理:左列代表前端的類型,右列代表服務(wù)端類型,每一行代表前端類型為X時,能從服務(wù)端下發(fā)的哪些類型中轉(zhuǎn)化,比如String 可以從 IntorFloat轉(zhuǎn)化。這幾個類型基本能覆蓋日常服務(wù)端下發(fā)的數(shù)據(jù),其它類型的轉(zhuǎn)化可根據(jù)自己的需求擴充。

 


前端
服務(wù)端
String Int,F(xiàn)loat
Float String
Double String
Bool String, Int
 

 

JSONDecoder沒有給我們便利的這種異常處理的API。如何解決呢?最直接的想法,在具體的model內(nèi)實現(xiàn)init(decoder: Decoder)手動解析可以實現(xiàn),但每個都這么處理太麻煩。

解決方案:KeyedDecodingContainer方法覆蓋

研究JSONDecoder的源碼,在解析自定義Model過程中,會發(fā)現(xiàn)這樣一個調(diào)用關(guān)系。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
// 入口方法
JSONDecoder decoder(type:Type data:Data)
 // 內(nèi)部類,真實用來解析的
 _JSONDecoder unbox(value:Any type:Type)
 // Model調(diào)用init方法
 Decodable init(decoder: Decoder)
 // 自動生成的init方法調(diào)用container
 Decoder container(keyedBy:CodingKeys)
 // 解析的容器
 KeyedDecodingContainer decoderIfPresent(type:Type) or decode(type:Type)
  // 內(nèi)部類,循環(huán)調(diào)用unbox
  _JSONDecoder unbox(value:Any type:Type)
  ...循環(huán),直到基本類型

最終的解析落到,_JSONDecoder的unbox 及 KeyedDecodingContainer的decoderIfPresent decode方法。但_JSONDecoder是內(nèi)部類,我們處理不了。最終決定對KeyedDecodingContainer下手,其中部分代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
extension KeyedDecodingContainer {
 .......
 /// Decode (Int, String) -> Int if possiable
 public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
  if let value = try? decode(type, forKey: key) {
   return value
  }
  if let value = try? decode(String.self, forKey: key) {
   return Int(value)
  }
  return nil
 }
 
 .......
 
 /// Avoid the failure just when decoding type of Dictionary, Array, SubModel failed
 public func decodeIfPresent<T>(_ type: T.Type, forKey key: K) throws -> T? where T : Decodable {
  return try? decode(type, forKey: key)
 }
}

上述代碼中,第一個函數(shù)decodeIfPresent(_ type: Int.Type, forKey key: K)是以key的信息解析出Int?值。這里覆蓋了KeyedDecodingContainer中的該函數(shù)的實現(xiàn),現(xiàn)在已try?的形式以Int類型解析,解析成功則直接返回,失敗則以String類型解析出一個StringValue,如果解析成功,再把String轉(zhuǎn)換成Int?值。

為什么要寫第二個函數(shù)呢?

場景:當(dāng)我們Model內(nèi)有其他的非基本類型的Model,比如其他自定義Model,Dictionary<String, Any>,Array<String>等,當(dāng)這些Model 類型不匹配或者出錯誤時也會拋出異常,導(dǎo)致整個大Model解析失敗。
覆蓋decodeIfPresent<T>(_ type: T.Type, forKey key: K)可以避免這些場景。至此,當(dāng)類型過程中出現(xiàn)解析的Optional類型出現(xiàn)不匹配時,我們要不是通過轉(zhuǎn)換,要不就是給其賦值nil,避免了系統(tǒng)此時直接throw exception導(dǎo)致退出整個解析過程的尷尬。

為何不覆蓋decode方法?decodeIfPresent可以返回Optional值,decode返回確定類型值。考慮到如果Model內(nèi)如果定義的類型是No-Optional型,那么可以認(rèn)為開發(fā)者確定該值必須存在,如果不存在Model很可能是錯誤的,所以直接fail。

完整擴展代碼點我

總結(jié)

Swift4.0 JSONDecoder確實為解析數(shù)據(jù)帶來了極大的便利。使用方式上類似Objective-C下的JSONModel。但實際開發(fā)中還是需要一些改造才能更好地服務(wù)于我們。

好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。

原文鏈接:https://juejin.im/post/5a928d2e5188257a7450d357

延伸 · 閱讀

精彩推薦
  • Swiftmac git xcrun error active developer path 錯誤

    mac git xcrun error active developer path 錯誤

    本文主要是講訴了如何解決在mac下使用git;xcode4.6的環(huán)境時,出現(xiàn)了錯誤(mac git xcrun error active developer path)的解決辦法,希望對大家有所幫助...

    Swift教程網(wǎng)2232020-12-16
  • SwiftSwift實現(xiàn)多個TableView側(cè)滑與切換效果

    Swift實現(xiàn)多個TableView側(cè)滑與切換效果

    這篇文章主要為大家詳細(xì)介紹了Swift實現(xiàn)多個TableView側(cè)滑與切換效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    乞力馬扎羅的雪雪5822021-01-08
  • SwiftSwift能代替Objective-C嗎?

    Swift能代替Objective-C嗎?

    這是我在網(wǎng)上上看到的答案,復(fù)制粘貼過來和大家分享一下,因為我和很多人一樣很關(guān)心Swift的出現(xiàn)對Mac開發(fā)的影響和對Objective-C的影響。...

    Swift教程網(wǎng)4412020-12-16
  • SwiftSwift使用CollectionView實現(xiàn)廣告欄滑動效果

    Swift使用CollectionView實現(xiàn)廣告欄滑動效果

    這篇文章主要為大家詳細(xì)介紹了Swift使用CollectionView實現(xiàn)廣告欄滑動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    Stevin的技術(shù)博客12372021-01-13
  • SwiftSwift中轉(zhuǎn)義閉包示例詳解

    Swift中轉(zhuǎn)義閉包示例詳解

    在Swift 中的閉包類似于結(jié)構(gòu)塊,并可以在任何地方調(diào)用,下面這篇文章主要給大家介紹了關(guān)于Swift中轉(zhuǎn)義閉包的相關(guān)資料,需要的朋友可以參考下...

    小小小_小朋友11412021-12-26
  • Swiftswift where與匹配模式的實例詳解

    swift where與匹配模式的實例詳解

    這篇文章主要介紹了swift where與匹配模式的實例詳解的相關(guān)資料,這里附有簡單的示例代碼,講的比較清楚,需要的朋友可以參考下...

    追到夢的魔術(shù)師14382021-01-06
  • SwiftSwift教程之基礎(chǔ)數(shù)據(jù)類型詳解

    Swift教程之基礎(chǔ)數(shù)據(jù)類型詳解

    這篇文章主要介紹了Swift教程之基礎(chǔ)數(shù)據(jù)類型詳解,本文詳細(xì)講解了Swift中的基本數(shù)據(jù)類型和基本語法,例如常量和變量、注釋、分號、整數(shù)、數(shù)值類型轉(zhuǎn)換等...

    Swift教程網(wǎng)5162020-12-18
  • SwiftSwift的74個常用內(nèi)置函數(shù)介紹

    Swift的74個常用內(nèi)置函數(shù)介紹

    這篇文章主要介紹了Swift的74個常用內(nèi)置函數(shù)介紹,這篇文章列舉出了所有的Swift庫函數(shù),內(nèi)置函數(shù)是指無需引入任何模塊即可以直接使用的函數(shù),需要的朋友可...

    Swift教程網(wǎng)5802020-12-19
主站蜘蛛池模板: 99在线观看国产 | 91精品啪在线观看国产日本 | 4hc44四虎www在线影院男同 | 青青热久免费精品视频网站 | 国产亚洲精aa在线观看不卡 | xxx中国bbbwww | 91精品啪在线观看国产线免费 | 欧美一区二区三区精品 | 91影视在线看免费观看 | 精品无人区一区二区三区 | 亚洲青草视频 | 日本成人黄色片 | 国内自拍网红在综合图区 | 欧美日韩国产亚洲一区二区三区 | 奇米色7777| 欧美精品久久一区二区三区 | 91麻豆制片厂 | 欧美日韩国产一区二区三区在线观看 | 国产精品久久久久影视不卡 | cosplay 极品videos | 水多多凹凸福利视频导航 | 狠狠干快播 | 美国美女hd18| 四虎网站网址 | 91精品免费观看老司机 | 门房秦大爷最新章节阅读 | 91久久偷偷做嫩草影院免费看 | 欧美成人免费观看bbb | 日本中文字幕在线精品 | 俺去俺去啦最新官网在线 | 天天做日日爱 | 国产第9页| 天天躁夜夜躁很很躁 | 国内亚州视频在线观看 | 免费视频精品一区二区三区 | 日本最新伦中文字幕 | 99精品视频在线观看免费播放 | 精品性影院一区二区三区内射 | 天天摸天天操天天爽 | 动漫精品午夜在线播放 | 国产视频一区二区 |