日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Swift之Codable自定义解析将任意数据类型解析为想要的类型

發(fā)布時間:2024/5/28 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Swift之Codable自定义解析将任意数据类型解析为想要的类型 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、前言

  • 大多數(shù)現(xiàn)代應(yīng)用程序的共同點是,它們需要對各種形式的數(shù)據(jù)進行編碼或解碼。無論是通過網(wǎng)絡(luò)下載的 Json 數(shù)據(jù),還是存儲在本地的模型的某種形式的序列化表示形式,對于幾乎無任何 Swift 代碼庫而言,能夠可靠地編碼和解碼不同的數(shù)據(jù)都是必不可少的。
  • 這就是為什么 Swift 的 Codable API 能成為 Swift 4.0 的新功能一部分時,具有如此重要的重要原因。從那時起,它已發(fā)展成為一種標準的、健壯的機制,可以在 Apple 的各種平臺中使用編碼和解碼包括服務(wù)器端 Swift。
  • Codable 之所以如此出色,是因為它與 Swift 工具鏈緊密集成,從而使編譯器可以自動合成大量編碼和解碼各種值所需的代碼。但是,有時需要自定義序列化時值的表示方式,那么該如何調(diào)整 Codable 實現(xiàn)來做到這一點呢?

二、Codable 自定義解析 Json

① 修改 Key

  • 可以通過修改用作序列化表示形式一部分的鍵來自定義類型的編碼和解碼方式。假設(shè)我們正在開發(fā)的一個核心數(shù)據(jù)模型如下所示:
struct Article: Codable {var url: URLvar title: Stringvar body: String }
  • 我們的模型當前使用完全自動合成的 Codable 實現(xiàn),這意味著其所有序列化鍵都將匹配其屬性的名稱。但是,將從中解碼 Article 值的數(shù)據(jù)(例如,從服務(wù)器下載的 Json)可能會使用略有不同的命名約定,從而導致默認解碼失敗。幸運的是,這一問題很容易解決,要自定義 Codable 在解碼(或編碼)的 Article 類型的實例時將使用哪些鍵,要做的就是在其中定義一個 CodingKeys 枚舉,并為與希望自定義的鍵匹配的大小寫分配自定義原始值。如下所示:
extension Article {enum CodingKeys: String, CodingKey {case url = "source_link"case title = "content_name"case body} }
  • 通過上述操作,可以繼續(xù)利用編譯器生成的默認實現(xiàn)進行實際的編碼工作,同時仍能更改將用于序列化的鍵的名稱。雖然上面的技術(shù)非常適合當我們想要使用完全自定義的鍵名時,但是如果只希望 Codable 使用屬性名的 snake_case 版本(例如,將 backgroundColor 轉(zhuǎn)換為 background_color),那么可以簡單地更改 Json 解碼器的 keyDecodingStrategy:
var decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase
  • 以上兩個 API 的優(yōu)點在于,它們能夠解決 Swift 模型與用于表示它們的數(shù)據(jù)之間的不匹配問題,而無需修改屬性名稱。

② 忽略 Key

  • 能夠自定義編碼鍵的名稱確實很有用,但有時我們可能希望完全忽略某些鍵。例如,現(xiàn)在正在開發(fā)一個記筆記應(yīng)用程序,并且使用戶能夠?qū)⒏鞣N筆記分組在一起以形成一個可以包括本地草稿的 NoteCollection:
struct NoteCollection: Codable {var name: Stringvar notes: [Note]var localDrafts = [Note]() }
  • 但是,雖然將 localDrafts 納入 NoteCollection 模型確實很方便,但可以說,我們不希望在序列化或反序列化此類集合時包含這些草稿。這樣做的原因可能是每次啟動應(yīng)用程序時為用戶提供整潔的狀態(tài),或者是因為服務(wù)器不支持草稿。幸運的是,這也可以輕松完成,而不必更改 NoteCollection 的實際 Codable 實現(xiàn),如果像以前一樣定義一個 CodingKeys 枚舉,而只是省略 localDrafts,那么在對 NoteCollection 值進行編碼或解碼時,將不會考慮該屬性:
extension NoteCollection {enum CodingKeys: CodingKey {case namecase notes} }
  • 為了使以上功能正常運行,要省略的屬性必須具有默認值,在這種情況下,localDrafts 已具有默認值。

③ 創(chuàng)建匹配的結(jié)構(gòu)

  • 到目前為止,只是在調(diào)整類型的編碼鍵,盡管這樣做通常可以受益匪淺,但有時需要對 Codable 自定義進行進一步的調(diào)整。假設(shè)正在構(gòu)建一個包含貨幣換算功能的應(yīng)用,并且正在將給定貨幣的當前匯率下載為 Json 數(shù)據(jù),如下所示:
{"currency": "PLN","rates": {"USD": 3.76,"EUR": 4.24,"SEK": 0.41} }
  • 然后,在 Swift 代碼中,想要將此類 Json 響應(yīng)轉(zhuǎn)換為 CurrencyConversion 實例,每個實例都包含一個 ExchangeRate 條目數(shù)組,每個幣種對應(yīng)一個:
struct CurrencyConversion {var currency: Currencyvar exchangeRates: [ExchangeRate] }struct ExchangeRate {let currency: Currencylet rate: Double }
  • 但是,如果僅僅只是使以上兩個模型都符合 Codable,將再次導致 Swift 代碼與要解碼的 Json 數(shù)據(jù)不匹配。但是這次,不只是關(guān)鍵字名稱的問題,結(jié)構(gòu)上有根本的不同。當然,可以修改 Swift 模型的結(jié)構(gòu),使其與 Json 數(shù)據(jù)的結(jié)構(gòu)完全匹配,但這并不總是可行的。盡管擁有正確的序列化代碼很重要,但是擁有適合實際代碼庫的模型結(jié)構(gòu)也同樣重要。
  • 相反,創(chuàng)建一個新的專用類型,它將在 Json 數(shù)據(jù)中使用的格式與 Swift 代碼的結(jié)構(gòu)體之間架起一座橋梁。在這種類型中,我們將能夠封裝將 Json 匯率字典轉(zhuǎn)換為一系列 ExchangeRate 模型所需的所有邏輯,如下所示:
private extension ExchangeRate {struct List: Decodable {let values: [ExchangeRate]init(from decoder: Decoder) throws {let container = try decoder.singleValueContainer()let dictionary = try container.decode([String : Double].self)values = dictionary.map { key, value inExchangeRate(currency: Currency(key), rate: value)}}} }
  • 使用上述類型,現(xiàn)在可以定義一個私有屬性,該名稱與用于其數(shù)據(jù)的 Json 密鑰相匹配,并使 exchangeRates 屬性僅充當該私有屬性的面向公眾的代理:
struct CurrencyConversion: Decodable {var currency: Currencyvar exchangeRates: [ExchangeRate] {return rates.values}private var rates: ExchangeRate.List }
  • 上面的方法起作用的原因是,在對值進行編碼或解碼時,永遠不會考慮計算屬性。當想使我們的 Swift 代碼與使用非常不同的結(jié)構(gòu)的 Json API 兼容時,上述技術(shù)可能是一個很好的工具,且無需完全從頭實現(xiàn) Codable。

④ 轉(zhuǎn)換值

  • 在解碼時,尤其是在使用無法控制的外部 Json API 進行解碼時,一個非常常見的問題是,以與 Swift 的嚴格類型系統(tǒng)不兼容的方式對類型進行編碼。例如,要解碼的 Json 數(shù)據(jù)可能使用字符串來表示整數(shù)或其他類型的數(shù)字。
  • 來看看一種可以處理這些值的方法,再次以一種自包含的方式,它不需要我們編寫完全自定義的 Codable 實現(xiàn)。本質(zhì)上想要做的是將字符串值轉(zhuǎn)換為另一種類型,以 Int 為例,將從定義一個協(xié)議開始,該協(xié)議可以將任何類型都標記為 StringRepresentable,這意味著可以將其轉(zhuǎn)換為字符串表示形式,也可以將其從字符串表示形式轉(zhuǎn)換為需要的類型:
protocol StringRepresentable: CustomStringConvertible {init?(_ string: String) }extension Int: StringRepresentable {}
  • 接下來,創(chuàng)建另一個專用類型,這是為任何可以由字符串支持的值,并讓它包含解碼和編碼一個值到字符串和從字符串編碼所需的所有代碼:
struct StringBacked<Value: StringRepresentable>: Codable {var value: Valueinit(from decoder: Decoder) throws {let container = try decoder.singleValueContainer()let string = try container.decode(String.self)guard let value = Value(string) else {throw DecodingError.dataCorruptedError(in: container,debugDescription: "Failed to convert an instance of \(Value.self) from '\(string)'")}self.value = value}func encode(to encoder: Encoder) throws {var container = encoder.singleValueContainer()try container.encode(value.description)} }
  • 就像以前為兼容 Json 的基礎(chǔ)存儲創(chuàng)建私有屬性的方式一樣,現(xiàn)在可以對編碼后由字符串后端的任何屬性執(zhí)行相同的操作,同時仍將數(shù)據(jù)適當?shù)毓_給其它 Swift 代碼類型,這是一個針對視頻類型的 numberOfLikes 屬性執(zhí)行此操作的示例:
struct Video: Codable {var title: Stringvar description: Stringvar url: URLvar thumbnailImageURL: URLvar numberOfLikes: Int {get { return likes.value }set { likes.value = newValue }}private var likes: StringBacked<Int> }
  • 在必須手動為屬性定義 setter 和 getter 的復雜性與必須回退到完全自定義的 Codable 實現(xiàn)的復雜性之間,這里肯定有一個折中,但是對于上述 Video 結(jié)構(gòu)體這樣的類型,它在其中僅具有一個屬性需要自定義,使用私有支持屬性可能是一個不錯的選擇。

三、Codable 將任意類型解析為想要的類型

① 常規(guī)解析

  • 默認情況下,使用 Swift 內(nèi)置的 Codable API 解析 Json 時,屬性類型需要和 Json 中的類型保持一致,否則就會解析失敗。
  • 例如,現(xiàn)有如下 Json:
{"name":"ydw","age":18 }
  • 開發(fā)中常用的模型如下:
struct User: Codable {var name: Stringvar age: Int }
  • 這個時候,正常解析則沒有任何問題,但是:
    • 當出現(xiàn)服務(wù)器將 age 中的 18 采用 String 方式 “18” 返回時,則無法解析,這是非常難遇見的情況;
    • 另一種常見的是返回 “18.1”, 這是一個 Double 類型,這時候一樣無法成功解析。
  • 在使用 OC 的時候,常用的方法將其解析為 NSString 類型,使用的時候再進行轉(zhuǎn)換,可是當使用 Swift 的 Codabel 時,就不能直接做到這樣。

② 如果服務(wù)器只會以 String 方式返回 Age,同時能確認里面是 Int 還是 Double

  • 可以使用上文中的“值轉(zhuǎn)換”來完成:
protocol StringRepresentable: CustomStringConvertible {init?(_ string: String) }extension Int: StringRepresentable {}struct StringBacked<Value: StringRepresentable>: Codable {var value: Valueinit(from decoder: Decoder) throws {let container = try decoder.singleValueContainer()let string = try container.decode(String.self)guard let value = Value(string) else {throw DecodingError.dataCorruptedError(in: container,debugDescription: "Failed to convert an instance of \(Value.self) from '\(string)'")}self.value = value}func encode(to encoder: Encoder) throws {var container = encoder.singleValueContainer()try container.encode(value.description)} }
  • 這個時候,模型如下:
{"name":"zhy","age":"18" }struct User: Codable {var name: Stringvar ageInt: Int {get { return age.value }set { age.value = newValue}}private var age: StringBacked<Int> }

③ 不能確認是什么類型

  • 第一種處理方法會改變原有數(shù)據(jù)結(jié)構(gòu),雖然對于直接重寫 User 的解析過程來說,擁有更多的通用性,但是遇到其它情況則束手無策。第二種方法同時也不會采用重寫模型自身的解析過程來實現(xiàn),那樣子不具備通用性,太麻煩,每次遇到都需要來一遍。
  • 參照第一種方法,先寫一個將任意類型轉(zhuǎn)換成 String? 的方法:
// 不確定服務(wù)器返回什么類型,都轉(zhuǎn)換為 String 然后保證正常解析 // 當前支持 Double Int String // 其他類型會解析成 nil // /// 將 String Int Double 解析為 String? 的包裝器 @propertyWrapper public struct YDWString: Codable {public var wrappedValue: String?public init(from decoder: Decoder) throws {let container = try decoder.singleValueContainer()var string: String?do {string = try container.decode(String.self)} catch {do {try string = String(try container.decode(Int.self))} catch {do {try string = String(try container.decode(Double.self))} catch {// 如果不想要 String? 可以在此處給 string 賦值 = “”string = nil}}}wrappedValue = string} }
  • 此時 User 寫成:
struct User: Codable {var name: String@YDWString public var age: String? }
  • 同理,可以寫一個 YDWInt,來將任意類型轉(zhuǎn)換為 Int,如果確實無法轉(zhuǎn)換,可以控制其為 nil 或者直接等于 0,這樣就可以保證不管怎么樣,解析不會失敗。此時 User 寫成:
struct User: Codable {var name: String@YDWInt public var age: Int }
  • 看起來這個地方影響很小,只有 User 解析失敗沒什么,當遇到整個頁面都是用一個 Json 返回時,不管是哪個局部出現(xiàn)問題,都會導致真?zhèn)€頁面解析失敗,所以還是要做好兼容操作最好。

總結(jié)

以上是生活随笔為你收集整理的Swift之Codable自定义解析将任意数据类型解析为想要的类型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。