Swift4 終極解析方案:基礎(chǔ)篇

轉(zhuǎn)載請(qǐng)注明,原文地址:Swift4 終極解析方案:基礎(chǔ)篇

做過(guò)網(wǎng)絡(luò)開(kāi)發(fā),特別是互聯(lián)網(wǎng),甚至移動(dòng)端開(kāi)發(fā)的,日常對(duì)于數(shù)據(jù)解析,早年主流的XML,現(xiàn)今主流的JSON都是非常熟悉的,說(shuō)道解析,系統(tǒng)自帶和各種第三方的解析庫(kù),除了解析當(dāng)然也當(dāng)不了懶癌的腳步,各種model反射庫(kù)。
對(duì)于Objective-C各種方案都尤為成熟,甚至還有專門的MacApp用于model生成,可以說(shuō)是懶到極致,好處當(dāng)然是節(jié)省出了擼貓擼手辦的時(shí)間(技術(shù)狗擼手辦不知道是什么時(shí)候開(kāi)始的惡習(xí),我還是更喜歡擼妹紙)。
這種操作對(duì)于Swift就比較蛋疼了,當(dāng)然第三方庫(kù)和工具也是完全夠用,但是,生成的model里面一大堆代碼,一個(gè)字,惡心。

那好,今年Swift更新到4.0版本之后帶了一個(gè)我最喜歡的功能:Codable協(xié)議。

CodableEncodableDecodable協(xié)議總和的別名。所以它既能編碼也能解碼,自從有了它,我model里面代碼奏是干干凈凈,清清爽爽,對(duì)于潔癖控來(lái)說(shuō),這貨是原生的,又可以少個(gè)PodPackage了,巴巴掌。

開(kāi)始吧

如果對(duì)此協(xié)議不太明白到底能干啥,可以先看下今年的WWDC視頻。

Codable讓我們可以通過(guò)StructClass不要一行多余代碼來(lái)解析JSONPlist數(shù)據(jù)。基礎(chǔ)庫(kù)簡(jiǎn)直嗨的不要不要的。
讓我們來(lái)看一個(gè)例子:

import Foundation

struct Swifter: Decodable {
  let fullName: String
  let id: Int
  let twitter: URL
}

let json = """
{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
}
""".data(using: .utf8)! // our data in native (JSON) format

let myStruct = try JSONDecoder().decode(Swifter.self, from: json) // Decoding our data
print(myStruct) // decoded!!!!!

如果你看過(guò)Decodable文檔,那你肯定知道里面有一個(gè)必須實(shí)現(xiàn)的方法init(from: Decoder)。然而示例里并沒(méi)實(shí)現(xiàn),照樣跑得飛起來(lái),那就是蘋果爸爸媽寶當(dāng)?shù)郊遥壕幾g器默認(rèn)會(huì)幫我們實(shí)現(xiàn)一個(gè)。

再看看另外一個(gè)例子:

import Foundation

enum BeerStyle : String, Codable {
    case ipa
    case stout
    case kolsch
    // ...
}

struct Beer {
    let name: String
    let brewery: String
    let style: BeerStyle
}

let json = """
{
    "name": "Endeavor",
    "abv": 8.9,
    "brewery": "Saint Arnold",
    "style": "ipa"
}
""".data(using: .utf8)! // our data in native (JSON) format

let myStruct = try JSONDecoder().decode(Swifter.self, from: json) // Decoding our data
print(myStruct) // decoded!!!!!

看到這里是不是就被戳到G點(diǎn)了???如果后端沒(méi)有啥特別的字段,你只需要把JSON里的Key作為Property即可。
解析的條件就是,只要是系統(tǒng)提供的如String,number,Bool以及各類集合,只要是符合Decodable協(xié)議即可,簡(jiǎn)直是嗨到極點(diǎn)。

自定義實(shí)現(xiàn)

能夠直接解析當(dāng)然是最好的,但是往往開(kāi)發(fā)的時(shí)候會(huì)遇到一些比較復(fù)雜的結(jié)構(gòu),那可能是ArrayDictionary相互嵌套,各個(gè)系統(tǒng)或者開(kāi)發(fā)語(yǔ)言的保留字導(dǎo)致字段奇特,以及很多煞筆后端搞些魔術(shù)字啊,拼音命名的字段啥的。

實(shí)際上開(kāi)發(fā)大多遇到的都是這種情況,那就不得不出動(dòng)自定義解析了。自定義的部分稍微復(fù)雜點(diǎn),坐穩(wěn)了別翻車。

解碼器

Decoder負(fù)責(zé)處理JSONPlist解析工作,需要重點(diǎn)關(guān)注的兩個(gè)方法:

public func container<Key>(keyedBy type: Key.Type) throws -> KeyedDecodingContainer<Key> where Key : CodingKey

public func singleValueContainer() throws -> SingleValueDecodingContainer

這兩個(gè)方法返回的都是Container。
第一個(gè)方法返回keyedContainer,KeyedDecodingContainer:如果我們要自定義解析,那就需要告訴解碼器如何映射。
第二個(gè)方法僅僅返回一個(gè)Container,而SingleValueDecodingContainer里的數(shù)據(jù)正是我們想要的。

容器

Decoder提供了基礎(chǔ)的功能解析原始數(shù)據(jù),自定義數(shù)據(jù)就需要我們自己來(lái)搞定。
KeyedDecodingContainer:我們的容器是通過(guò)鍵值匹配的,所以大可以看作[Key: Any]這樣的字典結(jié)構(gòu)。
不同的鍵對(duì)應(yīng)不同的類型數(shù)據(jù),所以容器提供的不同解碼方法:decode(Type:forKey:)
它的神奇之處就在于容器會(huì)自動(dòng)匹配數(shù)據(jù)類型。
當(dāng)然,解碼器也提供了通用方法:

public func decode<T>(_ type: T.Type, forKey key: KeyedDecodingContainer.Key) throws -> T where T : Decodable

有了這個(gè)泛型解析方法,那就意味著任意類型都可以匹配解析。

SingleValueDecodingContainer就更前面說(shuō)的一樣,只考慮返回。

實(shí)現(xiàn)

前面基礎(chǔ)已經(jīng)鋪墊完了,現(xiàn)在就不要編譯器幫忙了,我們自己手動(dòng)來(lái)實(shí)現(xiàn)init(from: Decoder)。

第一步:選擇正確的解碼器

示例里是JSON對(duì)象,那我們就選擇JSONDecoder。

let decoder = JSONDecoder()

JSONPlist解析器都是系統(tǒng)內(nèi)置的,如果你想要,你也可以自己實(shí)現(xiàn)一個(gè)解析器解析你夠奇葩的數(shù)據(jù)。

第二步:選擇正確的容器

JSON數(shù)據(jù)如下:

{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
}

其實(shí)按照最開(kāi)始說(shuō)的,這個(gè)對(duì)象可以直接反射,辣么我們這里講的自定義,那就按照自定義的套路走,我們按要求實(shí)現(xiàn)一個(gè)String結(jié)構(gòu)體,并且滿足CodingKey協(xié)議:

enum MyStructKeys: String, CodingKey {
  case fullName = "fullName"
  case id = "id"
  case twitter = "twitter"
}

接下來(lái)創(chuàng)建容器:

let container = try decoder.container(keyedBy: MyStructKeys.self)

第三步:提取數(shù)據(jù)

這里我們需要做類型轉(zhuǎn)換:

let fullName: String = try container.decode(String.self, forKey: .fullName)
let id: Int = try container.decode(Int.self, forKey: .id)
let twitter: URL = try container.decode(URL.self, forKey: .twitter)

第四步:初始化

使用默認(rèn)的構(gòu)造器:

let myStruct = Swifter(fullName: fullName, id: id, twitter: twitter)

現(xiàn)在我們就來(lái)看看全部實(shí)現(xiàn):

import Foundation

struct Swifter {
  let fullName: String
  let id: Int
  let twitter: URL
  
  init(fullName: String, id: Int, twitter: URL) { // default struct initializer
    self.fullName = fullName
    self.id = id
    self.twitter = twitter
  }
}

extension Swifter: Decodable {
  enum MyStructKeys: String, CodingKey { // declaring our keys 
    case fullName = "fullName"
    case id = "id"
    case twitter = "twitter"
  }
  
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: MyStructKeys.self) // defining our (keyed) container
    let fullName: String = try container.decode(String.self, forKey: .fullName) // extracting the data
    let id: Int = try container.decode(Int.self, forKey: .id) // extracting the data
    let twitter: URL = try container.decode(URL.self, forKey: .twitter) // extracting the data
    
    self.init(fullName: fullName, id: id, twitter: twitter) // initializing our struct
  }
}

let json = """
{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
}
""".data(using: .utf8)! // our native (JSON) data

let myStruct = try JSONDecoder().decode(Swifter.self, from: json) // decoding our data
print(myStruct) // decoded!

復(fù)雜結(jié)構(gòu)處理

數(shù)組

import Foundation

struct Swifter: Decodable {
  let fullName: String
  let id: Int
  let twitter: URL
}

let json = """
[{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
},{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
},{
 "fullName": "Federico Zanetello",
 "id": 123456,
 "twitter": "http://twitter.com/zntfdr"
}]
""".data(using: .utf8)! // our data in native format

let myStructArray = try JSONDecoder().decode([Swifter].self, from: json)

myStructArray.forEach { print($0) } // decoded!!!!!

字典

import Foundation

struct Swifter: Codable {
  let fullName: String
  let id: Int
  let twitter: URL
}

let json = """
{
  "one": {
    "fullName": "Federico Zanetello",
    "id": 123456,
    "twitter": "http://twitter.com/zntfdr"
  },
  "two": {
    "fullName": "Federico Zanetello",
    "id": 123456,
    "twitter": "http://twitter.com/zntfdr"
  },
  "three": {
    "fullName": "Federico Zanetello",
    "id": 123456,
    "twitter": "http://twitter.com/zntfdr"
  }
}
""".data(using: .utf8)! // our data in native format

let myStructDictionary = try JSONDecoder().decode([String: Swifter].self, from: json)

myStructDictionary.forEach { print("\($0.key): \($0.value)") } // decoded!!!!!

枚舉

import Foundation

struct Swifter: Decodable {
  let fullName: String
  let id: Int
  let twitter: URL
}

enum SwifterOrBool: Decodable {
  case swifter(Swifter)
  case bool(Bool)
}

extension SwifterOrBool: Decodable {
  enum CodingKeys: String, CodingKey {
    case swifter, bool
  }
  
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if let swifter = try container.decodeIfPresent(Swifter.self, forKey: .swifter) {
      self = .swifter(swifter)
    } else {
      self = .bool(try container.decode(Bool.self, forKey: .bool))
    }
  }
}

let json = """
[{
"swifter": {
   "fullName": "Federico Zanetello",
   "id": 123456,
   "twitter": "http://twitter.com/zntfdr"
  }
},
{ "bool": true },
{ "bool": false },
{
"swifter": {
   "fullName": "Federico Zanetello",
   "id": 123456,
   "twitter": "http://twitter.com/zntfdr"
  }
}]
""".data(using: .utf8)! // our native (JSON) data

let myEnumArray = try JSONDecoder().decode([SwifterOrBool].self, from: json) // decoding our data
  
myEnumArray.forEach { print($0) } // decoded!

嵌套結(jié)構(gòu)

import Foundation

struct Swifter: Decodable {
  let fullName: String
  let id: Int
  let twitter: URL
}

struct MoreComplexStruct: Decodable {
  let swifter: Swifter
  let lovesSwift: Bool
}

let json = """
{
    "swifter": {
        "fullName": "Federico Zanetello",
        "id": 123456,
        "twitter": "http://twitter.com/zntfdr"
    },
    "lovesSwift": true
}
""".data(using: .utf8)! // our data in native format

let myMoreComplexStruct = try JSONDecoder().decode(MoreComplexStruct.self, from: json)

print(myMoreComplexStruct.swifter) // decoded!!!!!

結(jié)尾

為了避免必要的情況,也為了增強(qiáng)代碼的健壯性,我們應(yīng)該多使用系統(tǒng)的錯(cuò)誤處理來(lái)避免經(jīng)常崩潰:

do {
  let myStruct = try JSONDecoder().decode(Swifter.self, from: json) // do your decoding here
} catch {
  print(error) // any decoding error will be printed here!
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,326評(píng)論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,664評(píng)論 4 61
  • 昨天沒(méi)有發(fā),今天的話生物1.5h 數(shù)學(xué)20min明天繼續(xù)寫 看書3h
    bia唧媽閱讀 276評(píng)論 0 0
  • 我不做太遙遠(yuǎn)的夢(mèng),這讓我的睡眠安恬;我不去緬懷往事,因?yàn)榛夭坏竭^(guò)去;我小心地去愛(ài)別人,因?yàn)槲也粣?ài)泛濫。我想哭的時(shí)候...
    高冷的小小櫻閱讀 268評(píng)論 0 1

友情鏈接更多精彩內(nèi)容