【问题标题】:Why does UnkeyedDecodingContainer require an additional outer array when decoding?为什么 UnkeyedDecodingContainer 在解码时需要额外的外部数组?
【发布时间】:2020-04-08 13:37:55
【问题描述】:

我正在尝试从 json 文件中解码正则表达式:

{
    ...
    "expressions" : [
        {"plus": [1, 2]},
        {"less": [{"plus": [3, 4]}, 5]}
    ],
    ...
}

我的目标是使用这种语法,以便尽可能清晰地输入它们。

最初我使用了 2 个键控枚举,1 个称为 expressionDesc,它可以是浮点数 (.f1)、常量 (.cnst) 或操作 (.op)。操作键的关联值是 operatorDesc 类型,它是第二个枚举。这包含运算符名称及其参数的表达式数组:

使用键控枚举意味着我必须像这样输入 json:

{
    ...
    "expressions" : [
        {"op": {"plus": [{"f1":1}, {"f1":2}]}},
        {"op": {"less": [{"op": {"plus": [{"f1":3}, {"f1":4}]}}, {"f1":5}]}}
    ],
    ...
}

这很难阅读,所以我尝试使用 UnkeyedDecodingContainer 来避免键入表达式类型键,然后仅根据给定的表达式类型进行解码:


enum operatorDesc : Decodable {
    case plus([expressionDesc])
    case less([expressionDesc])

    enum key: CodingKey { case plus; case less}
    enum CodingError: Error { case unknownFunction }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: key.self)
        do { let rawValue = try container.decode([expressionDesc].self, forKey: .plus); self = .plus(rawValue); print("Decoded \(self)") }
        catch { do { let rawValue = try container.decode([expressionDesc].self, forKey: .less); self = .less(rawValue); print("Decoded \(self)") }
                catch { throw CodingError.unknownFunction } }
    }
}

indirect enum expressionDesc : Decodable {
    case f1(f1)
    case const(String)
    case op(operatorDesc)
    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        do { let rawValue = try container.decode(Float.self); self = .f1(rawValue); print("Decoded \(self)") }
        catch { do { let rawValue = try container.decode(String.self); self = .const(rawValue); print("Decoded \(self)") }
                catch { do { let rawValue = try container.decode(operatorDesc.self); self = .op(rawValue); print("Decoded \(self)") }
                        catch {self = .f1(999); print("No expression found!") } } }
    }
}

struct sceneDesc : Decodable {
    ...
    let expressions : [expressionDesc]?
    ...
}

不幸的是,由于某种原因,这并没有完全给出我所期望的解码,并且当我尝试将表达式输入为顶级 json 格式时抛出“预期的数组并得到......”错误。但是我在表达式周围添加了额外的数组:

{
    ...
    "expressions" : [
        [{"plus": [[1], [2]]}],
        [{"less": [[{"plus": [[3], [4]]}], [5]]}]
    ],
    ...
}

这确实正确解码,输出:

Decoded f1(1.0)
Decoded f1(2.0)
Decoded plus([Mobius.expressionDesc.f1(1.0), Mobius.expressionDesc.f1(2.0)])
Decoded op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(1.0), Mobius.expressionDesc.f1(2.0)]))
Decoded f1(3.0)
Decoded f1(4.0)
Decoded plus([Mobius.expressionDesc.f1(3.0), Mobius.expressionDesc.f1(4.0)])
Decoded op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(3.0), Mobius.expressionDesc.f1(4.0)]))
Decoded f1(5.0)
Decoded less([Mobius.expressionDesc.op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(3.0), Mobius.expressionDesc.f1(4.0)])), Mobius.expressionDesc.f1(5.0)])
Decoded op(Mobius.operatorDesc.less([Mobius.expressionDesc.op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(3.0), Mobius.expressionDesc.f1(4.0)])), Mobius.expressionDesc.f1(5.0)]))

最终构造的表达式是:

Mobius.expressionDesc.op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(1.0), Mobius.expressionDesc.f1(2.0)]))

Mobius.expressionDesc.op(Mobius.operatorDesc.less([Mobius.expressionDesc.op(Mobius.operatorDesc.plus([Mobius.expressionDesc.f1(3.0), Mobius.expressionDesc.f1(4.0)])), Mobius.expressionDesc.f1(5.0)]))

哪些是正确的。

我想知道的是,有什么方法可以避免 json 中每个表达式周围需要额外的数组 [] 以允许它解码? (这些似乎只在表达式枚举中使用 UnkeyedDecodingContainer 时出现)

另外,如果有人知道更好的方法来实现我正在尝试的内容,我会全力以赴 - 当我添加更多操作时,嵌套的 do-catch 会变得非常难看,而且从我读过的内容来看,没有解决这个问题的简单方法。

【问题讨论】:

  • 哈哈,我发誓这个项目的其余部分并不是那么坚韧。带有可选结构的结构的问题是它允许给出多个值并且也没有给出值,而在这种情况下我希望它允许加或减,浮点或常量或操作。你是对的,虽然这并不令人愉快,天知道一旦它们被解码,我将如何处决它们。

标签: json swift codable decodable


【解决方案1】:

decoder.unkeyedContainer 方法用于解析数组。对于单个值,分别需要使用decode.singleValueContainer

indirect enum expressionDesc : Decodable {
    case f1(f1)
    case const(String)
    case op(operatorDesc)
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do { let rawValue = try container.decode(Float.self); self = .f1(rawValue); print("Decoded \(self)") }
        catch { do { let rawValue = try container.decode(String.self); self = .const(rawValue); print("Decoded \(self)") }
                catch { do { let rawValue = try container.decode(operatorDesc.self); self = .op(rawValue); print("Decoded \(self)") }
                        catch {self = .f1(999); print("No expression found!") } } }
    }
}

我尝试了你的代码并解析了第一个 json,它运行良好:

let text = """
{
    "expressions" : [
        {"plus": [1, 2]},
        {"less": [{"plus": [3, 4]}, 5]}
    ]
}
"""

print(try! JSONDecoder().decode(sceneDesc.self, from: text.data(using: .utf8)!))

结果:

sceneDesc(表达式: 可选([testapp.expressionDesc.op(testapp.operatorDesc.plus([testapp.expressionDesc.f1(1.0), testapp.expressionDesc.f1(2.0)])), testapp.expressionDesc.op(testapp.operatorDesc.less([testapp.expressionDesc.op(testapp.operatorDesc.plus([testapp.expressionDesc.f1(3.0), testapp.expressionDesc.f1(4.0)])), testapp.expressionDesc.f1(5.0)]))]))

【讨论】:

    猜你喜欢
    • 2015-08-24
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多