为什么 UnkeyedDecodingContainer 在解码时需要额外的外层数组?
Why does UnkeyedDecodingContainer require an additional outer array when decoding?
我正在尝试从 json 文件中解码正则表达式:
{
...
"expressions" : [
{"plus": [1, 2]},
{"less": [{"plus": [3, 4]}, 5]}
],
...
}
我设计这个语法是为了尽可能清楚地输入它们。
最初我使用了 2 个键控枚举,其中 1 个称为 expressionDesc,它可以是 float(.f1)、constant(.cnst) 或 operation(.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 格式时抛出了 "expected array and got ..." 错误。但是我在表达式周围添加了额外的数组:
{
...
"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 将变得非常丑陋,并且从我读过的内容来看没有简单的方法解决这个问题。
方法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(expressions:
Optional([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)]))]))
我正在尝试从 json 文件中解码正则表达式:
{
...
"expressions" : [
{"plus": [1, 2]},
{"less": [{"plus": [3, 4]}, 5]}
],
...
}
我设计这个语法是为了尽可能清楚地输入它们。
最初我使用了 2 个键控枚举,其中 1 个称为 expressionDesc,它可以是 float(.f1)、constant(.cnst) 或 operation(.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 格式时抛出了 "expected array and got ..." 错误。但是我在表达式周围添加了额外的数组:
{
...
"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 将变得非常丑陋,并且从我读过的内容来看没有简单的方法解决这个问题。
方法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(expressions: Optional([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)]))]))