Swift 具有动态键的可编码递归枚举
Swift codable recursive enum with dynamic keys
我正在尝试为动态数据编写结构。数据的键和它们的值都是未知的。该结构如下所示:
enum EntryData: Codable {
case string(String)
case array([EntryData]
case nested([String: EntryData])
}
struct Entry: Codable {
var data: [String: EntryData]
}
这样做的目标是能够像这样解码 JSON:
{
"data": {
"testA": "valueA",
"testB": ["valueB", ["valueC"]],
"testC": {
"testD": "valueD",
"testE": ["valueE", "valueF"]
}
}
}
并具有以下代码:
var data = EntryData(data: [
"testA": .string("valueA"),
"testB": .array([.string("valueB"), .array([.string("valueC")])]),
"testC": .nested([
"testD": .string("valueD"),
"testeE": .array([.string("valueE"), .string("valueF")])
])
])
编码到上面的 JSON 输出。
这在 Swift 中可行吗?如果是这样,实现会是什么样子?
非常感谢。
您可以在 EntryData
的每种情况下使用 singleValueContainer
到 decode/encode,而无需使用任何硬编码键。解码时,我们可以把这三种情况都尝试解码,看哪一种成功。
enum EntryData: Codable {
case string(String)
case array([EntryData])
case nested([String: EntryData])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let nested = try? container.decode([String: EntryData].self)
let array = try? container.decode([EntryData].self)
let string = try? container.decode(String.self)
switch (string, array, nested) {
case (let s?, nil, nil):
self = .string(s)
case (nil, let a?, nil):
self = .array(a)
case (nil, nil, let n?):
self = .nested(n)
default:
throw DecodingError.valueNotFound(
EntryData.self,
.init(codingPath: decoder.codingPath,
debugDescription: "Value must be either string, array or a dictionary!",
underlyingError: nil))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let s):
try container.encode(s)
case .array(let a):
try container.encode(a)
case .nested(let n):
try container.encode(n)
}
}
}
现在您可以:
let entry = try JSONDecoder().decode(Entry.self, from: data)
我正在尝试为动态数据编写结构。数据的键和它们的值都是未知的。该结构如下所示:
enum EntryData: Codable {
case string(String)
case array([EntryData]
case nested([String: EntryData])
}
struct Entry: Codable {
var data: [String: EntryData]
}
这样做的目标是能够像这样解码 JSON:
{
"data": {
"testA": "valueA",
"testB": ["valueB", ["valueC"]],
"testC": {
"testD": "valueD",
"testE": ["valueE", "valueF"]
}
}
}
并具有以下代码:
var data = EntryData(data: [
"testA": .string("valueA"),
"testB": .array([.string("valueB"), .array([.string("valueC")])]),
"testC": .nested([
"testD": .string("valueD"),
"testeE": .array([.string("valueE"), .string("valueF")])
])
])
编码到上面的 JSON 输出。
这在 Swift 中可行吗?如果是这样,实现会是什么样子?
非常感谢。
您可以在 EntryData
的每种情况下使用 singleValueContainer
到 decode/encode,而无需使用任何硬编码键。解码时,我们可以把这三种情况都尝试解码,看哪一种成功。
enum EntryData: Codable {
case string(String)
case array([EntryData])
case nested([String: EntryData])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let nested = try? container.decode([String: EntryData].self)
let array = try? container.decode([EntryData].self)
let string = try? container.decode(String.self)
switch (string, array, nested) {
case (let s?, nil, nil):
self = .string(s)
case (nil, let a?, nil):
self = .array(a)
case (nil, nil, let n?):
self = .nested(n)
default:
throw DecodingError.valueNotFound(
EntryData.self,
.init(codingPath: decoder.codingPath,
debugDescription: "Value must be either string, array or a dictionary!",
underlyingError: nil))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .string(let s):
try container.encode(s)
case .array(let a):
try container.encode(a)
case .nested(let n):
try container.encode(n)
}
}
}
现在您可以:
let entry = try JSONDecoder().decode(Entry.self, from: data)