7

这是我的代码:

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"]}

在这种情况下,解析器失败。它应该将 data 设置为 nil,但相反,它会尝试将其解码为 LoginUserResponseData 对象。

我在使用改造的 Android 上使用相同的方法,它工作正常。我宁愿不想将所有字段都设为可选。

有没有办法让解析器将空 json {} 视为 nil?或者将 LoginUserResponseData 设置为非可选的,它只有默认值?我知道我可以为此创建一个自定义解析器,但是我有大量这样的请求,并且需要太多额外的工作。

4

6 回答 6

8

就这么简单!

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)
于 2018-09-26T05:31:10.887 回答
3

这就是您的实现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
        }
    }
}
于 2018-02-23T08:33:39.830 回答
3

我的建议是解码resultenum并初始化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
}
于 2018-02-23T09:55:56.693 回答
2

这是因为{}它是一个空对象,但不是 nil。您有 2 个选项:

  1. 在服务器上更改为 returnnull而不是{}for datakey
  2. 实现自定义初始化程序init(from: Decoder)并手动处理这种情况
于 2018-02-23T08:22:40.747 回答
1

似乎不可能将 {} 视为 null,因此我创建了一个简单的工具来“修复”API 响应:

extension String {

    func replaceEmptyJsonWithNull() -> String {
        return replacingOccurrences(of: "{}", with: "null")
    }

}

@Vitaly Gozhenko 描述了其他方法并且应该使用,但我不能更改服务器 API,也不想编写完整的自定义解码器,因为这是一种情况。

于 2018-02-23T09:31:31.553 回答
0

哇,好吧,这根本不起作用。对不起。

几年后我才看到这篇文章,但每个解决方案都存在某些问题。更改 JSON 可能不切实际,消除错误try?可能会忽略其他可能合法的错误。

这是我通过扩展在项目中使用的建议解决方案 KeyedDecodingContainer:``` fileprivate extension KeyedDecodingContainer { private struct EmptyObject: Decodable {}

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).

于 2021-03-20T18:30:29.050 回答