这是我的代码:
class LoginUserResponse : Codable {
var result: String = ""
var data: LoginUserResponseData?
var mess: [String] = []
}
public class LoginUserResponseData : Codable {
var userId = "0"
var name = ""
}
现在,调用服务器API我正在解析这样的响应(使用Stuff库来简化解析):
do {
let loginUserResponse = try LoginUserResponse(json: string)
} catch let error {
print(error)
}
当我输入正确的密码时,我得到这样的答案:
{"result":"success","data":{"userId":"10","name":"Foo"},"mess":["You're logged in"]}
这很好,解析器工作正常。
虽然提供错误的密码会给出以下答案:
{"result":"error","data":{},"mess":["Wrong password"]}
在这种情况下,解析器失败。它应该将数据设置为nil,而是尝试将其解码为LoginUserResponseData对象。
我在Android上使用相同的方法使用改造,它工作正常。我宁愿不想将所有字段都设为可选字段。
有没有办法让解析器将空json {}视为nil?或者将LoginUserResponseData设置为非可选,它只有默认值?我知道我可以为此创建一个自定义解析器,但是我有很多这样的请求,这需要太多的额外工作。
答案 0 :(得分:2)
就这么简单!
class LoginUserResponse : Codable {
var result: String = ""
var data: LoginUserResponseData?
var mess: [String] = []
private enum CodingKeys: String, CodingKey {
case result, data, mess
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
result = try values.decode(String.self, forKey: .result)
mess = try values.decode([String].self, forKey: .mess)
data = try? values.decode(LoginUserResponseData.self, forKey: .data)
}
}
public class LoginUserResponseData : Codable {
var userId = "0"
var name = ""
}
let str = "{\"result\":\"success\",\"data\":{\"userId\":\"10\",\"name\":\"Foo\"},\"mess\":[\"You're logged in\"]}"
let str2 = "{\"result\":\"error\",\"data\":{},\"mess\":[\"Wrong password\"]}"
let decoder = JSONDecoder()
let result = try? decoder.decode(LoginUserResponse.self, from: str.data(using: .utf8)!)
let result2 = try? decoder.decode(LoginUserResponse.self, from: str2.data(using: .utf8)!)
dump(result)
dump(result2)
答案 1 :(得分:1)
我的建议是将result
解码为enum
,并在成功时初始化data
。
struct LoginUserResponse : Decodable {
enum Status : String, Decodable { case success, error }
private enum CodingKeys: String, CodingKey { case result, data, mess }
let result : Status
let data : UserData?
let mess : [String]
init(from decoder: Decoder) throws
{
let values = try decoder.container(keyedBy: CodingKeys.self)
result = try values.decode(Status.self, forKey: .result)
mess = try values.decode([String].self, forKey: .mess)
switch result {
case .success: data = try values.decode(UserData.self, forKey: .data)
case .error: data = nil
}
}
}
public struct UserData : Decodable {
let userId : String
let name : String
}
答案 2 :(得分:0)
这是因为{}
是一个空对象但不是零。
您有两个选择:
null
而不是{}
data
密钥init(from: Decoder)
并手动处理此案例答案 3 :(得分:0)
这是init(from: Decoder)
的实现方式。
注意:您应该考虑将LoginUserResponse
从类更改为结构,因为它只是存储值。
struct LoginUserResponse: Codable {
var result: String
var data: LoginUserResponseData?
var mess: [String]
init(from decoder: Decoder) throws
{
let values = try decoder.container(keyedBy: CodingKeys.self)
result = try values.decode(String.self, forKey: .result)
mess = try values.decode([String].self, forKey: .mess)
if let d = try? values.decode(LoginUserResponseData.self, forKey: .data) {
data = d
}
}
}
答案 4 :(得分:0)
哇,好吧,这根本行不通。对不起。
<块引用>我晚了几年才看到这篇文章,但有一些
每个解决方案的问题。更改 JSON 是潜在的
不切实际,用 try?
消除错误有可能
忽略其他可能合法的错误。
这是我通过扩展在项目中使用的建议解决方案
KeyedDecodingContainer
: ``` 文件私有扩展名
KeyedDecodingContainer {
私有结构 EmptyObject:可解码 {}
func decodePossibleEmptyObject<T: Decodable>(_ key: K) throws -> T? {
if let _ = try? decode(EmptyObject.self, forKey: key) {
return nil
}
return try self.decode(T.self, forKey: key)
} } ```
创建 EmptyObject
表示只允许 try?
成功
如果,事实上,对象是一个空对象。否则,解码器
将继续按要求解码对象,错误下降
通过方法。
最大的缺点是这需要自定义 init(from: Coder)
。
答案 5 :(得分:-1)
似乎无法将{}视为空,因此我已经创建了一个简单的工具来修复&#34; API响应:
extension String {
func replaceEmptyJsonWithNull() -> String {
return replacingOccurrences(of: "{}", with: "null")
}
}
其他方式由@Vitaly Gozhenko描述并应该使用,但我不能更改服务器API,也不想编写完整的自定义解码器,因为这是一个案例。