Swift Codable 嵌套 json 作为字符串

smu*_*uka 5 json swift swift4 codable

我用于后端调用的服务返回所有这些 json 结构:

{
    "status" : "OK",
    "payload" : **something**
}
Run Code Online (Sandbox Code Playgroud)

哪里的东西可以是一个简单的字符串:

{
    "status" : "OK",
    "payload" : "nothing changed"
}
Run Code Online (Sandbox Code Playgroud)

或嵌套的 json(具有任何属性的任何 json),例如:

{
    "status" : "OK",
    "payload" : {
                    "someInt" : 2,
                    "someString" : "hi",
                    ...
                }
}
Run Code Online (Sandbox Code Playgroud)

这是我的结构:

struct GenericResponseModel: Codable {

    let status:String?
    let payload:String?
}
Run Code Online (Sandbox Code Playgroud)

我想始终将“有效负载”解码为字符串。因此,在第二种情况下,我希望“GenericResponseModel”的有效负载属性包含该字段的 json 字符串,但是如果我尝试解码该响应,则会收到错误消息:

Type 'String' mismatch: Expected to decode String but found a dictionary instead
Run Code Online (Sandbox Code Playgroud)

可以存档我想要的吗?

非常感谢

Ash*_*lls 4

这个\xe2\x80\xa6怎么样

\n\n

声明PayloadType协议\xe2\x80\xa6

\n\n
protocol PayloadType: Decodable { }\n
Run Code Online (Sandbox Code Playgroud)\n\n

并 make String,并struct Payload符合它\xe2\x80\xa6

\n\n
extension String: PayloadType { }\n\nstruct Payload: Decodable, PayloadType {\n    let someInt: Int\n    let someString: String\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后制作GenericResponseModelgeneric\xe2\x80\xa6

\n\n
struct GenericResponseModel<T: PayloadType>: Decodable {\n\n    let status: String\n    let payload: T\n\n    enum CodingKeys: CodingKey {\n        case status, payload\n    }\n\n    init(from decoder: Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        status = try container.decode(String.self, forKey: .status)            \n        payload = try container.decode(T.self, forKey: .payload)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后就可以解码如下\xe2\x80\xa6

\n\n
let data = """\n{\n"status" : "OK",\n"payload" : "nothing changed"\n}\n""".data(using: .utf8)!\n\nprint(try JSONDecoder().decode(GenericResponseModel<String>.self, from: data))\n\n// GenericResponseModel<String>(status: "OK", payload: "nothing changed")\n
Run Code Online (Sandbox Code Playgroud)\n\n

和

\n\n
let data2 = """\n{\n"status" : "OK",\n"payload" : {\n"someInt" : 2,\n"someString" : "hi"\n}\n}\n""".data(using: .utf8)!\n\nprint(try JSONDecoder().decode(GenericResponseModel<Payload>.self, from: data2))\n\n// GenericResponseModel<Payload>(status: "OK", payload: Payload(someInt: 2, someString: "hi"))\n
Run Code Online (Sandbox Code Playgroud)\n\n


\n当然,这依赖于你payload提前知道类型。如果有效负载类型错误,您可以通过抛出特定错误来解决此问题\xe2\x80\xa6

\n\n
enum GenericResponseModelError: Error {\n    case wrongPayloadType\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后\xe2\x80\xa6

\n\n
init(from decoder: Decoder) throws {\n    let container = try decoder.container(keyedBy: CodingKeys.self)\n    status = try container.decode(String.self, forKey: .status)\n\n    do {\n        payload = try container.decode(T.self, forKey: .payload)\n    } catch {\n        throw GenericResponseModelError.wrongPayloadType\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后在解码\xe2\x80\xa6时处理这个错误

\n\n
let data = """\n{\n"status" : "OK",\n"payload" : {\n"someInt" : 2,\n"someString" : "hi"\n}\n}\n""".data(using: .utf8)!\n\ndo {\n    let response = try JSONDecoder().decode(GenericResponseModel<String>.self, from: data) // Throws\n    print(response) \n} catch let error as GenericResponseModelError where error == .wrongPayloadType {\n    let response = try JSONDecoder().decode(GenericResponseModel<Payload>.self, from: data2) // Success!\n    print(response)\n}\n
Run Code Online (Sandbox Code Playgroud)\n