如果我不确定密钥,我应该如何使用 JSONDecoder 解码 json object
How should I decode a json object using JSONDecoder if I am unsure of the keys
我有一个 api 以下形状的响应 -
{
"textEntries":{
"summary":{
"id":"101e9136-efd9-469e-9848-132023d51fb1",
"text":"some text",
"locale":"en_GB"
},
"body":{
"id":"3692b0ec-5b92-4ab1-bc25-7711499901c5",
"text":"some other text",
"locale":"en_GB"
},
"title":{
"id":"45595d27-7e06-491e-890b-f50a5af1cdfe",
"text":"some more text again",
"locale":"en_GB"
}
}
}
我想通过 JSONDecoder
对其进行解码,以便我可以使用这些属性。我面临的挑战是密钥,在这种情况下,summary
、body
和 title
是在其他地方生成的,并不总是这些值,它们总是唯一的,但基于发生在产品其他地方的逻辑,因此对不同内容文章的另一个调用可以 return leftBody
或 subTitle
等
这些道具 body 的模型始终相同,但是,我可以预期在任何响应组合中都存在相同的字段。
我需要能够访问其他地方代码中每个键的 body。不过,另一个 API 回复会告诉我所需的密钥。
我不确定如何用 Decodable
处理这个问题,因为我无法提前输入值。
我曾考虑过为 body -
建模之类的东西
struct ContentArticleTextEntries: Decodable {
var id: String
var text: String
var locale: Locale
}
并将值存储在类似 -
的结构中
struct ContentArticle: Decodable {
var textEntries: [String: ContentArticleTextEntries]
private enum CodingKeys: String, CodingKey {
case textEntries
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.textEntries = try values.decode(ContentArticleTextEntries.self, forKey: .textEntries)
}
}
我可以让他们在其他地方使用下标来访问 属性 但是我不知道如何解码成这种形状,因为上面的方法不起作用。
所以我稍后会像 textEntries["body"]
那样访问。
我也不知道有没有更好的方法来处理这个问题。
我曾考虑过使用枚举将键转换为 'type',但同样无法提前了解枚举情况。
我知道 textEntries 这不会改变,我知道 id、text 和 locale 这不会改变。它是我不知道的这一层之间的键。我已经尝试了@vadian 发布的有用的解决方案,但似乎无法在只需要解码一组密钥的情况下进行这项工作。
使用 "decodeIfPresent" 变体方法而不是解码方法,您还需要将 ContentArticleTextEntries 字典分解为单独的键:
struct ContentArticle: Decodable {
var id: String
var text: String?
var locale: String?
private enum CodingKeys: String, CodingKey {
case id, text, locale
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
self.text = try container.decodeIfPresent(String.self, forKey: .text)
self.locale = try container.decodeIfPresent(String.self, forKey: .locale)
}
}
如果你的模特是这样的,
struct ContentArticle: Decodable {
let textEntries: [String: ContentArticleTextEntries]
}
struct ContentArticleTextEntries: Decodable {
var id: String
var text: String
var locale: String
}
然后,您可以简单地访问基于 key
的数据,例如,
let response = try JSONDecoder().decode(ContentArticle.self, from: data)
let key = "summary"
print(response.textEntries[key])
注意:解析JSON.[=15时如果没有特殊处理,不用写enum CodingKeys
和init(from:)
=]
对于 中提出的解决方案,结构是
struct ContentArticleTextEntries: Decodable {
let title : String
let id: String
let text: String
let locale: Locale
enum CodingKeys: String, CodingKey {
case id, text, locale
}
init(from decoder: Decoder) throws {
self.title = try decoder.currentTitle()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.text = try container.decode(String.self, forKey: .text)
let localeIdentifier = try container.decode(String.self, forKey: .locale)
self.locale = Locale(identifier: localeIdentifier)
}
}
struct ContentArticle: TitleDecodable {
let title : String
var elements: [ContentArticleTextEntries]
}
struct Container: Decodable {
let containers: [ContentArticle]
init(from decoder: Decoder) throws {
self.containers = try decoder.decodeTitledElements(ContentArticle.self)
}
}
然后解码Container.self
我有一个 api 以下形状的响应 -
{
"textEntries":{
"summary":{
"id":"101e9136-efd9-469e-9848-132023d51fb1",
"text":"some text",
"locale":"en_GB"
},
"body":{
"id":"3692b0ec-5b92-4ab1-bc25-7711499901c5",
"text":"some other text",
"locale":"en_GB"
},
"title":{
"id":"45595d27-7e06-491e-890b-f50a5af1cdfe",
"text":"some more text again",
"locale":"en_GB"
}
}
}
我想通过 JSONDecoder
对其进行解码,以便我可以使用这些属性。我面临的挑战是密钥,在这种情况下,summary
、body
和 title
是在其他地方生成的,并不总是这些值,它们总是唯一的,但基于发生在产品其他地方的逻辑,因此对不同内容文章的另一个调用可以 return leftBody
或 subTitle
等
这些道具 body 的模型始终相同,但是,我可以预期在任何响应组合中都存在相同的字段。
我需要能够访问其他地方代码中每个键的 body。不过,另一个 API 回复会告诉我所需的密钥。
我不确定如何用 Decodable
处理这个问题,因为我无法提前输入值。
我曾考虑过为 body -
建模之类的东西struct ContentArticleTextEntries: Decodable {
var id: String
var text: String
var locale: Locale
}
并将值存储在类似 -
的结构中struct ContentArticle: Decodable {
var textEntries: [String: ContentArticleTextEntries]
private enum CodingKeys: String, CodingKey {
case textEntries
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.textEntries = try values.decode(ContentArticleTextEntries.self, forKey: .textEntries)
}
}
我可以让他们在其他地方使用下标来访问 属性 但是我不知道如何解码成这种形状,因为上面的方法不起作用。
所以我稍后会像 textEntries["body"]
那样访问。
我也不知道有没有更好的方法来处理这个问题。
我曾考虑过使用枚举将键转换为 'type',但同样无法提前了解枚举情况。
我知道 textEntries 这不会改变,我知道 id、text 和 locale 这不会改变。它是我不知道的这一层之间的键。我已经尝试了@vadian 发布的有用的解决方案,但似乎无法在只需要解码一组密钥的情况下进行这项工作。
使用 "decodeIfPresent" 变体方法而不是解码方法,您还需要将 ContentArticleTextEntries 字典分解为单独的键:
struct ContentArticle: Decodable {
var id: String
var text: String?
var locale: String?
private enum CodingKeys: String, CodingKey {
case id, text, locale
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
self.text = try container.decodeIfPresent(String.self, forKey: .text)
self.locale = try container.decodeIfPresent(String.self, forKey: .locale)
}
}
如果你的模特是这样的,
struct ContentArticle: Decodable {
let textEntries: [String: ContentArticleTextEntries]
}
struct ContentArticleTextEntries: Decodable {
var id: String
var text: String
var locale: String
}
然后,您可以简单地访问基于 key
的数据,例如,
let response = try JSONDecoder().decode(ContentArticle.self, from: data)
let key = "summary"
print(response.textEntries[key])
注意:解析JSON.[=15时如果没有特殊处理,不用写enum CodingKeys
和init(from:)
=]
对于
struct ContentArticleTextEntries: Decodable {
let title : String
let id: String
let text: String
let locale: Locale
enum CodingKeys: String, CodingKey {
case id, text, locale
}
init(from decoder: Decoder) throws {
self.title = try decoder.currentTitle()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.text = try container.decode(String.self, forKey: .text)
let localeIdentifier = try container.decode(String.self, forKey: .locale)
self.locale = Locale(identifier: localeIdentifier)
}
}
struct ContentArticle: TitleDecodable {
let title : String
var elements: [ContentArticleTextEntries]
}
struct Container: Decodable {
let containers: [ContentArticle]
init(from decoder: Decoder) throws {
self.containers = try decoder.decodeTitledElements(ContentArticle.self)
}
}
然后解码Container.self