Using Codable With Nested Arrays Is Easier And More Fun Than You Think

Using Codable With Nested Arrays Is Easier And More Fun Than You Think

https://medium.com/@nictheawesome/using-codable-with-nested-objects-is-easier-and-more-fun-than-you-think-8c9edfa29ed7

This is a follow-up to my article on using Codable with nested JSON trees. If you haven’t read that one, first catch up there since I’ll be piggy-backing on what I’ve covered in it. ????

  • 這是我關(guān)于將Codable與嵌套JSON樹一起使用的文章的后續(xù)內(nèi)容。 如果你還沒有讀過那個,那么首先趕上那里,因為我將捎帶我所涵蓋的內(nèi)容。????

So now that we’re all on the same page, let’s talk about the one thing everyone needs a little help with: HOW DO I GET AN ARRAY OF OBJECTS FROM JSON RESPONSE USING CODABLE OMG?!

  • 所以現(xiàn)在我們都在同一頁上,讓我們談?wù)劽總€人都需要幫助的一件事:我如何使用CODABLE OMG從JSON響應(yīng)中獲得一個對象陣列?!

(For you TL;DRers, the full code is at the bottom)

Yeah, this one stumped me for a while. And honestly, I think the solution isn’t amazing, but it works for now, and in some cases is actually still better than using JSONSerialization or a third-party library. So, let’s dig into it. Below is the data I’m using for this post (you’ll notice it’s a little trickier than last time):

  • 是的,這個讓我困擾了一會兒。 老實說,我認為解決方案并不令人驚訝,但它現(xiàn)在有效,在某些情況下實際上仍然比使用JSONSerialization或第三方庫更好。 那么,讓我們深入研究它。 以下是我在這篇文章中使用的數(shù)據(jù)(你會發(fā)現(xiàn)它比上次有點棘手):
{"Response": {
    "Bar": true,
    "Baz": "Hello, World!",
    "Friends": [
        {"FirstName": "Gabe",
        "FavoriteColor": "Orange"},
        {"FirstName": "Jeremiah",
        "FavoriteColor": "Green"},
        {"FirstName": "Peter",
        "FavoriteColor": "Red"}]}}

“Yikes, that’s trouble!” you might think. But actually, it’s not so bad! See, the real magic of Codable is in how it works. If you think back to the last article, we were able to decode properties from our JSON data simply by telling the decoder what container it’s in, what type we’re expecting out of it, and what key to use to get it. But what’s so magical about it is that Array will automatically conform to Codable if its elements also already conform to it. So just follow the same steps (tell the decoder to access a key in the container and get back the type you’re expecting), and voila! It’s really that simple.

  • “哎呀,這很麻煩!”你可能會想。 但實際上,它并沒有那么糟糕! 看,Codable真正的神奇之處在于它是如何工作的。 如果你回想一下上一篇文章,我們就可以通過告訴解碼器它所在的容器,我們期待它的類型以及使用它來獲取它的密鑰來解碼我們的JSON數(shù)據(jù)中的屬性。 但是它的神奇之處在于,如果Codable的元素也已經(jīng)符合它,它將自動符合Codable。 因此,只需按照相同的步驟(告訴解碼器訪問容器中的密鑰并返回您期望的類型),瞧! 這真的很簡單。

So let’s start with a model for a friend. We want an object that holds their first name and favorite color. Following the steps from the last article, it might look something like this:

  • 讓我們從一個朋友的模型開始吧。 我們想要一個擁有他們的名字和喜歡的顏色的對象。 按照上一篇文章中的步驟,它可能看起來像這樣:
struct Friend: Codable {
  let firstName: String
  let favoriteColor: String
  enum CodingKeys: String, CodingKey {
    case firstName = "FirstName"
    case favoriteColor = "FavoriteColor"
  }
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.firstName = try container.decode(String.self, forKey: .firstName)
    self.favoriteColor = try container.decode(String.self, forKey: .favoriteColor)
  }
  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self
    try container.encode(self.firstName, forKey: .firstName)
    try container.encode(self.favoriteColor, forKey: .favoriteColor)
  }
}

So this seems pretty straightforward, right? We are just telling the compiler how to create this object using a Decoder that we know we’re going to provide, and how to break it down using an Encoder that we also know we’re going to provide.

  • 所以這看起來很簡單,對吧? 我們只是告訴編譯器如何使用我們知道我們將要提供的解碼器來創(chuàng)建這個對象,以及如何使用我們也知道我們將要提供的編碼器來分解它。

?? DON’T MISS THIS ?? ???? !DO不要錯過這個!?????

You want to make sure you understand what’s going on here. This object is only prepared to work with data that looks like this:

  • 你想確保你明白這里發(fā)生了什么。 此對象僅準備使用如下所示的數(shù)據(jù):
{
  "FirstName": String,
  "FavoriteColor": String
}

BUT since this model conforms to Codable, Swift 4 automatically gives us an ability to make an Array<Friend> using the same tool! ????

  • 但是由于這個模型符合Codable,Swift 4自動讓我們能夠使用相同的工具制作一個Array <Friend>!????

So now our response model would look like this:

  • 所以現(xiàn)在我們的響應(yīng)模型看起來像這樣:
struct Response: Codable {
  let bar: Bool
  let baz: String
  let friends: [Friend]
  enum CodingKeys: String, CodingKey {
    case response = "Response"
    case bar = "Bar"
    case baz = "Baz"
    case friends = "Friends"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let response = try container.nestedContainer(keyedBy:
    CodingKeys.self, forKey: .response)
    self.bar = try response.decode(Bool.self, forKey: .bar)
    self.baz = try response.decode(String.self, forKey: .baz)
    self.friends = try response.decode([Friend].self, forKey: .friends)
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    var response = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
    try response.encode(self.bar, forKey: .bar)
    try response.encode(self.baz, forKey: .baz)
    var friends = response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
   try friends.encode(self.friends, forKey: .friends)
   }
}

There you have it! Of course, the most practical example of this is when you get back JSON data that has a root container of "Response" with an array of objects, but this approach will work just the same.

  • 你有它! 當然,最實際的例子是當你獲得具有一個對象數(shù)組的根容器“響應(yīng)”的JSON數(shù)據(jù)時,但這種方法將起到同樣的作用。

And that’s the waaaaaaaaaaaaaaay the news goes!

  • 這就是新聞發(fā)布的waaaaaaaaaaaaaay!

FULL CODE:

let jsonString = """
{"Response": {
  "Bar": true,
  "Baz": "Hello, World!",
  "Friends": [
    {"FirstName": "Gabe",
    "FavoriteColor": "Orange"},
    {"FirstName": "Jeremiah",
    "FavoriteColor": "Green"},
    {"FirstName": "Peter",
    "FavoriteColor": "Red"}]}}
"""
struct Friend: Codable {
  let firstName: String
  let favoriteColor: String
  enum CodingKeys: String, CodingKey {
    case firstName = "FirstName"
    case favoriteColor = "FavoriteColor"
  }
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.firstName = try container.decode(String.self, forKey: .firstName)
    self.favoriteColor = try container.decode(String.self, forKey: .favoriteColor)
  }
    func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    try container.encode(self.firstName, forKey: .firstName)
    try container.encode(self.favoriteColor, forKey: .favoriteColor)
  }
}
struct Response: Codable {
  let bar: Bool
  let baz: String
  let friends: [Friend]
  enum CodingKeys: String, CodingKey {
    case response = "Response"
    case bar = "Bar"
    case baz = "Baz"
    case friends = "Friends"
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let response = try container.nestedContainer(keyedBy:
    CodingKeys.self, forKey: .response)
    self.bar = try response.decode(Bool.self, forKey: .bar)
    self.baz = try response.decode(String.self, forKey: .baz)
    self.friends = try response.decode([Friend].self, forKey: .friends)
  }

  func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    var response = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
    try response.encode(self.bar, forKey: .bar)
    try response.encode(self.baz, forKey: .baz)
    var friends = response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
   try friends.encode(self.friends, forKey: .friends)
   }
}
let data = jsonString.data(using: .utf8)!
// Initializes a Response object from the JSON data at the top.
let myResponse = try! JSONDecoder().decode(Response.self, from: data)
// Turns your Response object into raw JSON data you can send back!
let dataToSend = try! JSONEncoder().encode(myResponse)

Note:

I mentioned at the beginning that I don’t think this is an amazing solution, and that’s only half-true. In this instance, this obviously works great, but what would be really nice would be to expand Codable so that you could simply provide a specific key or even a path and tell the compiler to give you an object or array of objects at that path. In this example, if you don’t get or have need for the "Bar" and "Baz" properties, it kinda sucks to have to still create a Response object simply to hold your [Friends]. This is something that’s available in Objective-C and other third-party libraries like SwiftyJSON, and it would be nice to see that functionality here as well. I guess we’ll see what happens! ˉ_(ツ)_/ˉ

  • 我在開始時提到過,我認為這不是一個令人驚訝的解決方案,而且這只是半真的。 在這個例子中,這顯然很有效,但是真正好的是擴展Codable以便您可以簡單地提供特定的鍵甚至路徑并告訴編譯器在該路徑上為您提供對象或?qū)ο髷?shù)組。 在這個例子中,如果你沒有或者不需要“Bar”和“Baz”屬性,那么只需要創(chuàng)建一個Response對象就可以保存你的[Friends]。 這是在Objective-C和其他第三方庫(如SwiftyJSON)中可用的東西,在這里也可以看到這個功能。 我想我們會看到會發(fā)生什么!ˉ\ (ツ)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,858評論 0 10
  • The Inner Game of Tennis W Timothy Gallwey Jonathan Cape ...
    網(wǎng)事_79a3閱讀 12,945評論 3 20
  • 也許直到現(xiàn)在的我!才真正的明白了多年前太姥姥臨別前眼神的真正意義! 太多的不舍,太多的放不下,太多的慈愛,太多的含...
    四夕夢雅閱讀 375評論 0 2
  • 近日,因受同學英年早逝的影響,常常反思,竟總是想起小時候吃過,記憶中的那些美食。一一從記憶深處挖掘出來,總有做出來...
    摩西奶奶的粉絲閱讀 396評論 2 5
  • 跟好閨蜜聊天,發(fā)現(xiàn)她原本覺得不可思議的2017年家庭收入計劃,居然完成了。 而我,在最該賺錢的年紀,選擇了休息.....
    最后的十一閱讀 218評論 0 0

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