如何在 Swift 中编写更精简的 Codable 结构
How to write leaner Codable Structs in Swift
我的问题是关于结构的。
现状:
我有一个从文件系统读取的 JSON 对象。此数据有 3 个方面。
- 根级密钥,即
notConcerned0ne
和 notConcernedTwo
仅在运行时已知。
- 二级属性已知,即。
PredeterminedOne
和 PredeterminedTwo
已知,后代属性也已知。
{
"notConcerned0ne": {
"PredeterminedOne": {
"label": "blue",
"description": "something..."
}
},
"notConcernedTwo": {
"PredeterminedTwo": {
"label": "green",
"description": "something..."
}
}
}
我简化了我的数据,实际上,至少会有20个预定属性。
以下工作正常。但是鉴于我将拥有更多预定属性,我正在寻找一种解决方案来减少执行相同操作的重复结构。
// working
struct Domain: Codable {
let notConcerned0Ne: NotConcerned0Ne
let notConcernedTwo: NotConcernedTwo
enum CodingKeys: String, CodingKey {
case notConcerned0Ne = "notConcerned0ne"
case notConcernedTwo = "notConcernedTwo"
}
}
struct NotConcerned0Ne: Codable {
let predeterminedOne: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedOne = "PredeterminedOne"
}
}
struct Predetermined: Codable {
let label, predeterminedDescription: String
enum CodingKeys: String, CodingKey {
case label
case predeterminedDescription = "description"
}
}
struct NotConcernedTwo: Codable {
let predeterminedTwo: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedTwo = "PredeterminedTwo"
}
}
下面是我的尝试,就是减少重复代码。
struct Domain: Codable {
let notConcerned0Ne, notConcernedTwo: NotConcernedShared
enum CodingKeys: String, CodingKey {
case notConcerned0Ne = "notConcerned0ne"
case notConcernedTwo = "notConcernedTwo"
}
}
struct NotConcernedShared: Codable {
let predeterminedOne, predeterminedTwo: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedOne = "PredeterminedOne"
case predeterminedTwo = "PredeterminedTwo"
}
}
struct Predetermined: Codable {
let label, predeterminedDescription: String
enum CodingKeys: String, CodingKey {
case label
case predeterminedDescription = "description"
}
}
但这会出现错误
keyNotFound(CodingKeys(stringValue: "PredeterminedTwo", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "notConcerned0ne", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"PredeterminedTwo\", intValue: nil) (\"PredeterminedTwo\").", underlyingError: nil))
keyNotFound(CodingKeys(stringValue: "PredeterminedTwo", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "notConcerned0ne", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"PredeterminedTwo\", intValue: nil) (\"PredeterminedTwo\").", underlyingError: nil))
请您帮忙指出一些我明显遗漏的有关可编码协议的具体信息。或者以我形成结构的方式。
此外,也许我可以删除根级密钥并执行 typealias TheDomain = [String: Domain]
,因为我不关心根级 属性 名称。
如果你想看的话,我的加载函数工作正常。
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
您可以将 json 解码为字典字典,其中内部字典使用 Predetermined
作为值
let values = try JSONDecoder().decode([String: [String: Predetermined]].self, from: data)
.map(\.value)
然后您可以轻松查找“PredeterminedOne/Two”
的正确元素
如果你不关心“PredeterminedOne/Two”(我不确定这个)那么你也可以使用.flatMap
直接得到一个Predetermined
的数组,只是将其添加到上面的代码中
let values = try JSONDecoder().decode([String: [String: Predetermined]].self, from: data)
.map(\.value)
.flatMap(\.values)
我的问题是关于结构的。
现状:
我有一个从文件系统读取的 JSON 对象。此数据有 3 个方面。
- 根级密钥,即
notConcerned0ne
和notConcernedTwo
仅在运行时已知。 - 二级属性已知,即。
PredeterminedOne
和PredeterminedTwo
已知,后代属性也已知。
{
"notConcerned0ne": {
"PredeterminedOne": {
"label": "blue",
"description": "something..."
}
},
"notConcernedTwo": {
"PredeterminedTwo": {
"label": "green",
"description": "something..."
}
}
}
我简化了我的数据,实际上,至少会有20个预定属性。
以下工作正常。但是鉴于我将拥有更多预定属性,我正在寻找一种解决方案来减少执行相同操作的重复结构。
// working
struct Domain: Codable {
let notConcerned0Ne: NotConcerned0Ne
let notConcernedTwo: NotConcernedTwo
enum CodingKeys: String, CodingKey {
case notConcerned0Ne = "notConcerned0ne"
case notConcernedTwo = "notConcernedTwo"
}
}
struct NotConcerned0Ne: Codable {
let predeterminedOne: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedOne = "PredeterminedOne"
}
}
struct Predetermined: Codable {
let label, predeterminedDescription: String
enum CodingKeys: String, CodingKey {
case label
case predeterminedDescription = "description"
}
}
struct NotConcernedTwo: Codable {
let predeterminedTwo: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedTwo = "PredeterminedTwo"
}
}
下面是我的尝试,就是减少重复代码。
struct Domain: Codable {
let notConcerned0Ne, notConcernedTwo: NotConcernedShared
enum CodingKeys: String, CodingKey {
case notConcerned0Ne = "notConcerned0ne"
case notConcernedTwo = "notConcernedTwo"
}
}
struct NotConcernedShared: Codable {
let predeterminedOne, predeterminedTwo: Predetermined
enum CodingKeys: String, CodingKey {
case predeterminedOne = "PredeterminedOne"
case predeterminedTwo = "PredeterminedTwo"
}
}
struct Predetermined: Codable {
let label, predeterminedDescription: String
enum CodingKeys: String, CodingKey {
case label
case predeterminedDescription = "description"
}
}
但这会出现错误
keyNotFound(CodingKeys(stringValue: "PredeterminedTwo", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "notConcerned0ne", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"PredeterminedTwo\", intValue: nil) (\"PredeterminedTwo\").", underlyingError: nil))
keyNotFound(CodingKeys(stringValue: "PredeterminedTwo", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "notConcerned0ne", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"PredeterminedTwo\", intValue: nil) (\"PredeterminedTwo\").", underlyingError: nil))
请您帮忙指出一些我明显遗漏的有关可编码协议的具体信息。或者以我形成结构的方式。
此外,也许我可以删除根级密钥并执行 typealias TheDomain = [String: Domain]
,因为我不关心根级 属性 名称。
如果你想看的话,我的加载函数工作正常。
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
您可以将 json 解码为字典字典,其中内部字典使用 Predetermined
作为值
let values = try JSONDecoder().decode([String: [String: Predetermined]].self, from: data)
.map(\.value)
然后您可以轻松查找“PredeterminedOne/Two”
的正确元素如果你不关心“PredeterminedOne/Two”(我不确定这个)那么你也可以使用.flatMap
直接得到一个Predetermined
的数组,只是将其添加到上面的代码中
let values = try JSONDecoder().decode([String: [String: Predetermined]].self, from: data)
.map(\.value)
.flatMap(\.values)