1

我正在尝试为动态数据编写一个结构。数据的键是未知的,它们的值也是未知的。结构如下所示:

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 中可能吗?如果是这样,实现会是什么样子?

提前谢谢了。

4

1 回答 1

2

您可以使用singleValueContainer解码/编码的每个案例EntryData,而无需使用任何硬编码键。解码时,我们可以尝试将三种情况都解码,看看哪一种成功了。

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)
于 2021-12-16T18:17:01.927 回答