Google Question: swift what is nestedContainer
https://medium.com/@nictheawesome/using-codable-with-nested-json-is-both-easy-and-fun-19375246c9ff
So Codable is new, fresh, and rad. But when I first started learning how to use it, I was surprised how little information there was available for people using nested JSON structures.
- 所以Codable是新的,新鮮的,并且很棒。 但是當(dāng)我第一次開始學(xué)習(xí)如何使用它時(shí),我發(fā)現(xiàn)可用的信息很少對那些想要使用嵌套JSON結(jié)構(gòu)的人。
TL;DR: just scroll to the bottom for the full code.
One StackOverflow answer even said you should encompass your model in another model for the server response, then just access your desired model via the initialized parent. Which is gross, don’t do that. (EDIT: sometimes this is appropriate, like if you’re parsing out several objects that all tie together and need to be initialized at the same time. I personally still don’t like it, but you do you.)
- 一個(gè)StackOverflow回答甚至說你應(yīng)該在另一個(gè)模型中包含你的模型用于服務(wù)器響應(yīng),然后只需通過初始化的父模型訪問你想要的模型。 這是嚴(yán)重的,不要那樣做。 (編輯:有時(shí)這是合適的,就像你解析出幾個(gè)全部聯(lián)系在一起并需要同時(shí)初始化的對象一樣。我個(gè)人仍然不喜歡它,但是你做到了。)
So, I’m writing this to share with the world that getting at deeper levels of JSON data with Codable is both easy, and, as aforementioned, fun!
- 所以,我寫這篇文章是為了與全世界分享,用Codable獲得更深層次的JSON數(shù)據(jù)既簡單又如前所述,很有趣!
So first let’s just take a look at a simple model.
- 首先讓我們來看一個(gè)簡單的模型。
struct Foo: Codable {
let bar: Bool
let baz: String
let bestFriend: String
let funnyGuy: String
let favoriteWeirdo: String
}
As you can see, this is nothing too complicated. But what if we need to construct it from JSON data that looks like this?
- 如你所見,這并不復(fù)雜。 但是,如果我們需要從看起來像這樣的JSON數(shù)據(jù)構(gòu)建它呢?
{"Response": {
"Bar": true,
"Baz": "Hello, World!",
"Friends": {
"Best": "Nic",
"FunnyGuy": "Gabe",
"FavoriteWeirdo": "Jer"
}
}
}
?? Whatever shall we do?!
- ??無論我們做什么?!
Well, fortunately, Codable has some pretty nifty tricks up its sleeve, and it’s all built in for you! All you need to know is how to ask for it. First, let’s declare our CodingKeys enum inside our model so the API knows what keys we’re working with here, especially since they are different than the property names we want to be using.
- 嗯,幸運(yùn)的是,Codable有一些非常漂亮的技巧,而且它都是為你內(nèi)置的! 您需要知道的是如何使用它。 首先,讓我們在模型中聲明我們的CodingKeys枚舉,以便API知道我們在這里使用的鍵,特別是因?yàn)樗鼈兣c我們想要使用的屬性名稱不同。
enum CodingKeys: String, CodingKey {
case response = "Response"
case bar = "Bar"
case baz = "Baz"
case friends = "Friends"
case bestFriend = "Best"
case funnyGuy = "FunnyGuy"
case favoriteWeirdo = "FavoriteWeirdo"
}
A quick note on the naming convention here: You can name these cases anything you want, but unless you’re going to declare the values explicitly, they need to perfectly match the keys in the JSON data. So just to clarify, if you’re providing the values, you can name them whatever you’d like, but you have the option of simply naming them exactly as they come back in the JSON tree and save yourself some work.
- 關(guān)于命名約定的快速說明:您可以將這些情況命名為您想要的任何名稱,但除非您要明確聲明這些值,否則它們需要與JSON數(shù)據(jù)中的鍵完全匹配。 所以只是為了澄清一下,如果你提供了這些值,你可以隨心所欲地命名它們,但你可以選擇簡單地命名它們,就像它們回到JSON樹中一樣,并且你也可以省點(diǎn)事。
You’ll also notice that we have declared keys for everything in our tree. Let’s take a look at how to build our model using our data and CodingKeys!
- 您還會(huì)注意到我們已經(jīng)為樹中的所有內(nèi)容聲明了鍵。 我們來看看如何使用我們的數(shù)據(jù)和CodingKeys構(gòu)建我們的模型!
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let response = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
bar = try response.decode(Bool.self, forKey: .bar)
baz = try response.decode(String.self, forKey: .baz)
let friends = try response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
bestFriend = try friends.decode(String.self, forKey: .bestFriend)
funnyGuy = try friends.decode(String.self, forKey: .funnyGuy)
favoriteWeirdo = try friends.decode(String.self, forKey: .favoriteWeirdo)
}
Whew! Let’s break this down. First, the container is basically our root of the JSON tree we’re working with here. Think of it as the outer-most set of brackets. Knowing that, it makes sense why we need to get to the response (by the key of “Response”), because technically that is already nested within our root container! After that it’s a breeze to get Bar and Baz out, but here comes another container! This is where the brilliance of this API really shines through: you just tell it you need another nested container! With Friends there, we can safely and easily access our last properties.
- 呼! 讓我們打破這個(gè)。 首先,容器基本上是我們在這里使用的JSON樹的根。 可以把它想象成最外面的括號(hào)。 知道了,為什么我們需要得到響應(yīng)(通過“響應(yīng)”的鍵)是有道理的,因?yàn)榧夹g(shù)上已經(jīng)嵌套在我們的根容器中! 在那之后,讓Bar和Baz退出是一件輕而易舉的事,但這里有另一個(gè)容器! 這就是這個(gè)API的亮點(diǎn)真正發(fā)揮作用的地方:你只需告訴它你需要另一個(gè)嵌套容器! 有了朋友,我們可以安全輕松地訪問我們的最后一個(gè)屬性。
Piece of ??, right?!
- 一塊??,對吧?!
Now, let’s take a look at how we take our pretty model and turn it into Data that we can send back to the server!
- 現(xiàn)在,讓我們來看看我們?nèi)绾尾捎梦覀兊钠聊P筒⑵滢D(zhuǎn)換為可以發(fā)送回服務(wù)器的數(shù)據(jù)!
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(bar, forKey: .bar)
try response.encode(baz, forKey: .baz)
var friends = response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
try friends.encode(bestFriend, forKey: .bestFriend)
try friends.encode(funnyGuy, forKey: .funnyGuy)
try friends.encode(favoriteWeirdo, forKey: .favoriteWeirdo)
}
As you can see, you just follow the same steps! Set up the base container, then get to your Response nested container. Throw in your top-level properties, then get the nested container for your friends. Throw those in there, and voila! You’re now a master of handling nested dictionaries in a JSON tree with Codable!
- 如您所見,您只需按照相同的步驟操作即可! 設(shè)置基本容器,然后轉(zhuǎn)到Response嵌套容器。 扔進(jìn)您的頂級(jí)屬性,然后為您的朋友獲取嵌套容器。 把那些扔進(jìn)那里,瞧! 您現(xiàn)在是使用Codable在JSON樹中處理嵌套字典的大師!
Now that you’re a pro, take it to the next level! ????
- 既然你是專業(yè)人士,那就把它提升到一個(gè)新的水平!????
Here’s the code you can throw into a playground to see how it all works together:
- 這是你可以扔進(jìn)游樂場的代碼,看看它們是如何一起工作的:
let jsonString = """
{"Response": {
"Bar": true,
"Baz": "Hello, World!",
"Friends": {
"Best": "Nic",
"FunnyGuy": "Gabe",
"FavoriteWeirdo": "Jer"
}
}
}
"""
let data = jsonString.data(using: .utf8)!
struct Foo: Codable {
// MARK: - Properties
let bar: Bool
let baz: String
let bestFriend: String
let funnyGuy: String
let favoriteWeirdo: String
// MARK: - Codable
// Coding Keys
enum CodingKeys: String, CodingKey {
case response = "Response"
case bar = "Bar"
case baz = "Baz"
case friends = "Friends"
case bestFriend = "Best"
case funnyGuy = "FunnyGuy"
case favoriteWeirdo = "FavoriteWeirdo"
}
// Decoding
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let response = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
bar = try response.decode(Bool.self, forKey: .bar)
baz = try response.decode(String.self, forKey: .baz)
let friends = try response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
bestFriend = try friends.decode(String.self, forKey: .bestFriend)
funnyGuy = try friends.decode(String.self, forKey: .funnyGuy)
favoriteWeirdo = try friends.decode(String.self, forKey: .favoriteWeirdo)
}
// Encoding
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(bar, forKey: .bar)
try response.encode(baz, forKey: .baz)
var friends = response.nestedContainer(keyedBy: CodingKeys.self, forKey: .friends)
try friends.encode(bestFriend, forKey: .bestFriend)
try friends.encode(funnyGuy, forKey: .funnyGuy)
try friends.encode(favoriteWeirdo, forKey: .favoriteWeirdo)
}
}
let myFoo = try! JSONDecoder().decode(Foo.self, from: data)
// Initializes a Foo object from the JSON data at the top.
let dataToSend = try! JSONEncoder().encode(myFoo)
// Turns your Foo object into raw JSON data you can send back!