使用 JSON 解码器用空对象解码 JSON
Decode JSON with empty object using JSONDecoder
我想使用 JSONDecoder
解码 JSON。它按预期工作,但是对于内部对象为空的 JSON JSONDecoder
会抛出错误 The data couldn’t be read because it is missing.
示例 JSON 错误:
{
"header": {
"code": 1053,
"message": "Incorrect information."
},
"body": {}
}
样本 JSON 成功:
{
"header": {
"code": 1053
"message": "Nice information."
},
"body": {
"client_id": 12345
}
}
成功JSON,轻松破译。但是在错误 JSON 上,它抛出错误。
这是我使用的代码
struct ApiResponse: Decodable {
let header: Header
let body: Body
struct Header: Decodable {
let responseCode: Int
let message: String
}
struct Body: Decodable {
let clientId: Int
}
}
let decoder: JSONDecoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode(ApiResponse.self, from: data)
您始终可以实现自己的解码功能,让您可以做任何您想做的事情,但解决这个问题的快速方法只是将无法保证 return 的任何内容标记为可选。
请记住,如果密钥是由服务器 return 编辑的,那么它必须正确解码。通常我会建议使 Body
可选..但是在这里它不起作用.
在这种情况下,您需要执行以下操作:
struct ApiResponse: Decodable {
let header: Header
let body: Body // key is being returned so we keep it as part of the response
struct Header: Decodable {
let code: Int // match your variables with the JSON being returned
let message: String
}
struct Body: Decodable {
let clientId: Int? // sometimes nothing comes inside the body dict, so make its internals optional
}
}
编辑:
此外,正如 Leo 在评论中指出的那样,您还犯了一个愚蠢的错误,即您的变量实际上没有与响应匹配。请注意,您的 JSON 将 code
作为键,而您的 Header
对象正在寻找 responseCode
我已经编辑了我的原始回复以进行此更改。
您可以扩展 KeyedDecodingContainer
以使用协议
将空字典视为 nil
public protocol EmptyDictionaryRepresentable {
associatedtype CodingKeys : RawRepresentable where CodingKeys.RawValue == String
associatedtype CodingKeyType: CodingKey = Self.CodingKeys
}
extension KeyedDecodingContainer {
public func decodeIfPresent<T>(_ type: T.Type, forKey key: KeyedDecodingContainer.Key) throws -> T?
where T : Decodable & EmptyDictionaryRepresentable
{
guard contains(key) else { return nil }
let container = try nestedContainer(keyedBy: type.CodingKeyType.self, forKey: key)
return container.allKeys.isEmpty ? nil : try decode(T.self, forKey: key)
}
}
要使用它,请采用协议并将受影响的 属性 声明为可选的
struct ApiResponse: Decodable {
let header: Header
let body: Body?
}
struct Body: Decodable, EmptyDictionaryRepresentable {
enum CodingKeys : String, CodingKey { case clientId = "client_id" }
let clientId: Int
}
警告:此解决方案不适用于 .convertFromSnakeCase
策略
注意:考虑 Header
中的键 - 结构成员名称不匹配
声明 clientId
为可选 属性。因为在你的错误 JSON client_id
不存在。有关详细信息,请阅读 Article.
我想使用 JSONDecoder
解码 JSON。它按预期工作,但是对于内部对象为空的 JSON JSONDecoder
会抛出错误 The data couldn’t be read because it is missing.
示例 JSON 错误:
{
"header": {
"code": 1053,
"message": "Incorrect information."
},
"body": {}
}
样本 JSON 成功:
{
"header": {
"code": 1053
"message": "Nice information."
},
"body": {
"client_id": 12345
}
}
成功JSON,轻松破译。但是在错误 JSON 上,它抛出错误。
这是我使用的代码
struct ApiResponse: Decodable {
let header: Header
let body: Body
struct Header: Decodable {
let responseCode: Int
let message: String
}
struct Body: Decodable {
let clientId: Int
}
}
let decoder: JSONDecoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode(ApiResponse.self, from: data)
您始终可以实现自己的解码功能,让您可以做任何您想做的事情,但解决这个问题的快速方法只是将无法保证 return 的任何内容标记为可选。
请记住,如果密钥是由服务器 return 编辑的,那么它必须正确解码。通常我会建议使 Body
可选..但是在这里它不起作用.
在这种情况下,您需要执行以下操作:
struct ApiResponse: Decodable {
let header: Header
let body: Body // key is being returned so we keep it as part of the response
struct Header: Decodable {
let code: Int // match your variables with the JSON being returned
let message: String
}
struct Body: Decodable {
let clientId: Int? // sometimes nothing comes inside the body dict, so make its internals optional
}
}
编辑:
此外,正如 Leo 在评论中指出的那样,您还犯了一个愚蠢的错误,即您的变量实际上没有与响应匹配。请注意,您的 JSON 将 code
作为键,而您的 Header
对象正在寻找 responseCode
我已经编辑了我的原始回复以进行此更改。
您可以扩展 KeyedDecodingContainer
以使用协议
nil
public protocol EmptyDictionaryRepresentable {
associatedtype CodingKeys : RawRepresentable where CodingKeys.RawValue == String
associatedtype CodingKeyType: CodingKey = Self.CodingKeys
}
extension KeyedDecodingContainer {
public func decodeIfPresent<T>(_ type: T.Type, forKey key: KeyedDecodingContainer.Key) throws -> T?
where T : Decodable & EmptyDictionaryRepresentable
{
guard contains(key) else { return nil }
let container = try nestedContainer(keyedBy: type.CodingKeyType.self, forKey: key)
return container.allKeys.isEmpty ? nil : try decode(T.self, forKey: key)
}
}
要使用它,请采用协议并将受影响的 属性 声明为可选的
struct ApiResponse: Decodable {
let header: Header
let body: Body?
}
struct Body: Decodable, EmptyDictionaryRepresentable {
enum CodingKeys : String, CodingKey { case clientId = "client_id" }
let clientId: Int
}
警告:此解决方案不适用于 .convertFromSnakeCase
策略
注意:考虑 Header
声明 clientId
为可选 属性。因为在你的错误 JSON client_id
不存在。有关详细信息,请阅读 Article.