用 Codable 協議實現快速 JSON 解析

2021-08-16 09:16:38 字數 4730 閱讀 2310

如果你之前用過objective-c的話, 那麼你一定對nsjsonserialization並不陌生。 它的總體步驟大致是這樣,先從data物件中解析出nsdictionarynsarray, 然後在從這裡面按照屬性名稱取出需要的值,最後再用這些值給實體物件賦值。

總體來說這個操作並不輕鬆,而且比較容易出差錯,比如你在寫解析**的時候把屬性名寫錯了,或者某個nil判斷沒有處理正確,導致了程式意外崩潰,就需要花不少時間進行除錯。

codable

我們的主題自然不是nsjsonserialization, 而是 swift 中提供的codable協議。 它和前者有著相似的作用,但應用範圍更廣,並且易用性更好。 先來看一下codable協議的定義:

typealias codable = decodable

& encodable

它其實另外兩個protocol的集合,也就是decodableencodable。 乙個用作資料解析,另乙個用作資料編碼。 其他不多說,咱們先來看乙個例項,我們先宣告乙個實體類person它宣告實現了codable

struct person : codable

除了宣告codable之外,這個實體類並沒有其他**,只有幾個屬性宣告。 如果我們需要把他的例項編碼成 json 字串,可以這樣:

letperson = person(name:

"swift", gender:

"male", age:

24)let

encoder = jsonencoder()

letdata = try! encoder.encode(person)

letencodedstring = string(data: data, encoding: .utf8)!

print(encodedstring) // 輸出

如上所示,首先初始化了乙個person例項。 然後初始化了乙個jsonencoder。 再呼叫它的encode方法,把 person 例項進行編碼。 讓後整個 json 編碼操作就完成了。

再來看看如何解析:

letjsonstring =

""let

jsondata = jsonstring.data(using: .utf8)!

letdecoder = jsondecoder()

letresult = try! decoder.decode(person.self, from: jsondata)

print(result) // 輸出: person(name:

"swift", gender:

"female", age:

22)解析的時候用的是 jsondecoder 物件,給他的 decode 方法傳入要解析的例項型別 -person.self,,再加上要解析的資料物件jsondata就完成了 json 資料的解析。

使用codable協議就是這麼簡單, 你不需要些任何具體的解析**,只需要你的實體類屬性名和 json 資料能夠對應上,就完成了內容的解析。 這樣相比nsjsonserialization來看,精簡了很多,並且不容易出錯。

這裡只有一點需要注意,對於我們剛才例子中的person類,除了它自己實現codable協議之外,它的所有屬性也必須是遵循codable的。 swift 系統庫中的string,int,double,date,url,data這些類都是實現了codable的。 如果你的自定義屬性是其他型別,則需要注意一下它是否也實現了codable

另外, 除了jsonencoderjsondecoder之外, swift 還為其他型別的資料提供了編譯碼能力, 比如propertylistencoder可以編碼 plist 資料格式。

對指定屬性編碼

預設情況下,如果宣告繼承了codable協議,這個例項中的所有屬性都會被算作編碼範圍內。 如果你只想對一部分屬性進行編譯碼,也是有辦法的,可以在你的自定義類中宣告乙個 codingkeys 列舉屬性:

struct

person : codable

}還是之前的person類,這次我們加入了 codingkeys 屬性,並且定義了兩個列舉值nameage,只有在 codingkeys 中指定的屬性名才會進行編碼,如果我們再次對person進行編碼,得到的將會是這樣的結果:

可以看到, gender 屬性由於沒有在codingkeys中宣告,所以不會被編碼。 另外如果使用了codingkeys,那些沒有在codingkeys中宣告的屬性就必須要要有乙個預設值,我們上面的**中其實給 gender 屬性也宣告了預設值。

我們還可以使用codingkeys改變編碼屬性的名稱:

struct

person : codable

}還是以person為例,這次我們在codingkeys列舉中講 name 屬性重新定義為 title。 這個意思就是說,雖然在person類中,這個屬性名還是name, 但在編碼後的 json 中,它的屬性名就應該是title

對上面這個類執行編碼後,得到的結果是這樣:

json 中的第乙個屬性名變成了title, 它對應 person 類中的name屬性。

自定義編碼過程

你還可以自定義整個編碼和解碼過程。 對於稍複雜一些的資料結構,這個能力還是會經常用到的。 比如我們想給 person 再加上身高和體重兩個屬性:

struct

person : codable

enum

bodykeys: string, codingkey

}這裡面新增的heightwidth屬性,分別對應體重和身高。 並且還增加了另外乙個屬性bodykeys。 為什麼要新增這個屬性呢? 是因為我們這次準備把heightwidth放到乙個單獨的物件中。 下面這樣解釋可能會更直觀一些,如果我們不新增bodykeys屬性,而是把他們直接定義到codingkeys裡面,那麼生成的 json 結構大致是這樣:

但我們單獨為heightweight定義了bodykeys列舉屬性。 並且把它有宣告到了codingkeys中。 這次codingkeys多了乙個body屬性,它對應的就是bodykeys這個列舉。 至於這個對應關係怎麼確立的,稍後會講到

學什麼技術好。}

這樣我想應該就說明了bodykeys的作用了。 這樣宣告完還不行,我們還需要手動的確立他們之間的對應關係,這就要過載codable的兩個方法:

extension person

func encode(to encoder: encoder) throws

}init(from decoder: decoder)用於解析資料,encode(to encoder: encoder)方法用於編碼資料。 上面的**我想不用過多解釋,很容易理解。

decoder.container()方法首先獲取 codingkey 的對應關係,這裡我們首先傳入codingkeys.self表示我們先前宣告的型別。 然後呼叫vals.decode()方法,用於解析某個單獨的屬性。 接下來呼叫vals.nestedcontainer()方法獲取內嵌的層級,也就是我們先前宣告的bodykeys。然後繼續解析。

編碼的相關處理也大同小異,把上面解碼方法中的邏輯反向處理了一遍。

這樣,如果我們對新的person例項再進行編碼,得到的將會是這樣的結果:

}可以看到,生成了帶層級的 json 資料。

總結codable協議的設計,可以幫助我們產出更好的**結構。對於簡單的資料模型,不需要任何處理即可使用。 而稍複雜的資料結構,也只需要將解析規則封裝到實體類中,可以有效避免**結構的散亂。

總之,像是資料解析這類的操作,在平時的開發工作中還是比較多的。 如果你正在開發 swift 專案,它是乙個你值得了解的特性。

用 Codable 協議實現快速 JSON 解析

refer struct person codable 上面的 塊除了宣告 codable 之外,這個實體類並沒有其他 只有幾個屬性宣告。如果我們需要把他的例項編碼成 json 字串,可以這樣 let person person name swift gender male age 24 let e...

swift中使用Codable協議實現json解析

typealias codable decodable encodable 它其實另外兩個 protocol 的集合,也就是 decodable 和 encodable。乙個用作資料解析,另乙個用作資料編碼。建立乙個實體類,只宣告幾個屬性,將它例項編碼為json字串 struct person co...

JavaScript中實現快速xml轉json

src js objtree.js script head 解析過程只需兩行 就能完成,且能正常解析包含多級childrennodes的xml,如下 var xml 1.0 encoding utf 8 var xotree new xml.objtree var jsondata xotree.p...