具有嵌套空数组的 JSON 的可编码 Swift 结构
Codable Swift Structs for JSON with Nested Empty Array
我有以下 Swift 代码和我正在尝试解码的一些示例 JSON。您可以将其放入 Playground 中亲自尝试:
let json = """
{
"sessionState": "abc",
"methodResponses": [
[
"Mailbox/get",
{
"state": "92",
"accountId": "xyz"
},
"0"
]
]
}
"""
let data = json.data(using: .utf8)!
if let object = try? JSONDecoder().decode(JMAPResponse.self, from: data) {
print(object)
}else{
print("JMAP decode failed")
}
我不知道如何处理嵌套数组,所以 I used quicktype.io,但它生成的仍然不起作用。
这是我的嵌套数组的根元素:
//Root
struct JMAPResponse: Codable{
var sessionState: String?
var methodResponses: [[JMAPResponseChild]]
}
这是 QuickType 建议我用来处理 methodResponse
节点的 enum
:
//Nested array wrapper
enum JMAPResponseChild: Codable{
case methodResponseClass(JMAPMethodResponse)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
if let x = try? container.decode(JMAPMethodResponse.self) {
self = .methodResponseClass(x)
return
}
throw DecodingError.typeMismatch(JMAPResponseChild.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JMAPResponseChild"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .methodResponseClass(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
这是下一层:
//No-key data structure (Mailbox/get, {}, 0)
struct JMAPMethodResponse: Codable{
var method: String
var data: [JMAPMailboxList]
var id: String
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(method)
try container.encode(data)
try container.encode(id)
}
}
最后,最低级别的节点:
struct JMAPMailboxList: Codable{
var state: String?
var accountId: String?
}
仍然无法成功解码结构。谁能看出我做错了什么?
您的 JMAPResponseChild
正在解码需要 String
或 JMAPMethodResponse
的数组。该数组如下所示:
[
"Mailbox/get",
{
"state": "92",
"accountId": "xyz"
},
"0"
]
它实际上包含 String
或 JMAPMailboxList
(不是 JMAPMethodResponse
)。如果将 JMAPResponseChild
中的所有 JMAPMethodResponse
引用更改为 JMAPMailboxList
,它应该可以工作。您将必须进行一些 post 处理或更改您的代码以将该三个值的数组转换为 JMAPMethodResponse
.
您的 JMapMethodResponse
结构可能更像这样:
struct JMAPMethodResponse: Codable {
var method: String
var data: JMAPMailboxList
var id: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let values = try container.decode([JMAPResponseChild].self)
guard case .string(let extractedMethod) = values[0] else {
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "First array element not String"))
}
method = extractedMethod
guard case .methodResponseClass(let extractedData) = values[1] else {
throw DecodingError.typeMismatch(JMAPMailboxList.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Second array element not JMAPMailboxList"))
}
data = extractedData
guard case .string(let extractedId) = values[2] else {
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Third array element not String"))
}
id = extractedId
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(JMAPResponseChild.string(method))
try container.encode(JMAPResponseChild.methodResponseClass(data))
try container.encode(JMAPResponseChild.string(id))
}
}
然后你可以在你的回复中得到一个数组:
struct JMAPResponse: Codable{
var sessionState: String?
var methodResponses: [JMAPMethodResponse]
}
我不知道编码器或解码器是否保证数组中的顺序。如果他们不这样做,这可能会随机中断。
我有以下 Swift 代码和我正在尝试解码的一些示例 JSON。您可以将其放入 Playground 中亲自尝试:
let json = """
{
"sessionState": "abc",
"methodResponses": [
[
"Mailbox/get",
{
"state": "92",
"accountId": "xyz"
},
"0"
]
]
}
"""
let data = json.data(using: .utf8)!
if let object = try? JSONDecoder().decode(JMAPResponse.self, from: data) {
print(object)
}else{
print("JMAP decode failed")
}
我不知道如何处理嵌套数组,所以 I used quicktype.io,但它生成的仍然不起作用。
这是我的嵌套数组的根元素:
//Root
struct JMAPResponse: Codable{
var sessionState: String?
var methodResponses: [[JMAPResponseChild]]
}
这是 QuickType 建议我用来处理 methodResponse
节点的 enum
:
//Nested array wrapper
enum JMAPResponseChild: Codable{
case methodResponseClass(JMAPMethodResponse)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
if let x = try? container.decode(JMAPMethodResponse.self) {
self = .methodResponseClass(x)
return
}
throw DecodingError.typeMismatch(JMAPResponseChild.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JMAPResponseChild"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .methodResponseClass(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
这是下一层:
//No-key data structure (Mailbox/get, {}, 0)
struct JMAPMethodResponse: Codable{
var method: String
var data: [JMAPMailboxList]
var id: String
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(method)
try container.encode(data)
try container.encode(id)
}
}
最后,最低级别的节点:
struct JMAPMailboxList: Codable{
var state: String?
var accountId: String?
}
仍然无法成功解码结构。谁能看出我做错了什么?
您的 JMAPResponseChild
正在解码需要 String
或 JMAPMethodResponse
的数组。该数组如下所示:
[
"Mailbox/get",
{
"state": "92",
"accountId": "xyz"
},
"0"
]
它实际上包含 String
或 JMAPMailboxList
(不是 JMAPMethodResponse
)。如果将 JMAPResponseChild
中的所有 JMAPMethodResponse
引用更改为 JMAPMailboxList
,它应该可以工作。您将必须进行一些 post 处理或更改您的代码以将该三个值的数组转换为 JMAPMethodResponse
.
您的 JMapMethodResponse
结构可能更像这样:
struct JMAPMethodResponse: Codable {
var method: String
var data: JMAPMailboxList
var id: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let values = try container.decode([JMAPResponseChild].self)
guard case .string(let extractedMethod) = values[0] else {
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "First array element not String"))
}
method = extractedMethod
guard case .methodResponseClass(let extractedData) = values[1] else {
throw DecodingError.typeMismatch(JMAPMailboxList.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Second array element not JMAPMailboxList"))
}
data = extractedData
guard case .string(let extractedId) = values[2] else {
throw DecodingError.typeMismatch(String.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Third array element not String"))
}
id = extractedId
}
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(JMAPResponseChild.string(method))
try container.encode(JMAPResponseChild.methodResponseClass(data))
try container.encode(JMAPResponseChild.string(id))
}
}
然后你可以在你的回复中得到一个数组:
struct JMAPResponse: Codable{
var sessionState: String?
var methodResponses: [JMAPMethodResponse]
}
我不知道编码器或解码器是否保证数组中的顺序。如果他们不这样做,这可能会随机中断。