如何在 Swift 中解码类型为 JSON 字典的 属性?

How to decode a property with type of JSON dictionary in Swift?

我目前在测试中遇到以下错误:

如果我正确理解了问题,那么我需要将我的字典转换为解码器。

目前我有下一个代码:

static var validConfirmAuthorizationData: [String: Any] { return json("valid_confirm_authorization_data") }

具有以下结构:

{
    "data": {
        "id": "1",
        "success": true
    }
}

以及我与解码器一起使用的响应 class 本身:

public struct SEConfirmAuthorizationResponse: Decodable {
    public let id: String
    public let success: Bool

    enum CodingKeys: String, CodingKey {
        case data
    }

    enum DataCodingKeys: String, CodingKey {
        case id
        case success
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let dataContainer = try container.nestedContainer(keyedBy: DataCodingKeys.self, forKey: .data)
        id = try dataContainer.decode(String.self, forKey: .id)
        success = try dataContainer.decode(Bool.self, forKey: .success)
    }
}

您有几个选择,您可以创建一个带有名为数据的变量的可编码结构。数据将具有 SEConfirmAuthorizationResponse 类型。然后你会解码成新的结构。

您可以像这样解码成字典:

let decoded = JsonDecoder().decode([String: SEConfirmAuthorizationResponse].self, from: someData)

let response = decoded[“data”]

或者您可以编写一个我通常尽量避免的自定义解码器。

原来是这样解决问题的

首先,我创建了SpecDecodableModel:

public struct SpecDecodableModel<T: Decodable> {
    static func create(from fixture: [String: Any]) -> T {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategyFormatters = [DateUtils.dateFormatter, DateUtils.ymdDateFormatter]
        let fixtureData = Data(fixture.jsonString!.utf8)
        return try! decoder.decode(T.self, from: fixtureData)
    }
}

之后,我可以将 JSON 结构转换为所需的类型。

现在回到最初的问题,我可以按如下方式解决:

let fixture = DataFixtures.validConfirmAuthorizationData
let response = SpecDecodableModel<SEConfirmAuthorizationResponse>.create(from: fixture)

更新:

public extension Dictionary {

    var jsonString: String? {
        if let data = try? JSONSerialization.data(withJSONObject: self, options: []),
            let string = String(data: data, encoding: String.Encoding.utf8) {
            return string
        }
        return nil
    }
}
public struct AuthorizationData: Codable {
    public let id: String
    public let success: Bool
    
    init(id: String, success: Bool) {
        self.id = id
        self.success = success
    }
    
    enum CodingKeys: String, CodingKey {
        case id
        case success
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(String.self, forKey: .id)
        success = try container.decode(Bool.self, forKey: .success)
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(success, forKey: .success)
    }
}

public struct SEConfirmAuthorizationResponse: Codable {
    public let data: AuthorizationData
    
    enum CodingKeys: String, CodingKey {
        case data
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        data = try container.decode(AuthorizationData.self, forKey: .data)
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(data, forKey: .data)
    }
    
    init(_ data: AuthorizationData) {
        self.data = data
    }
}

func authData() -> Data? {
    let authorizationData = AuthorizationData(id: "1", success: true)
    let response = SEConfirmAuthorizationResponse(authorizationData)
    
    guard let prettyJsonData = try? JSONEncoder().encode(response) else {
        return nil
    }
    if let jsonString = String(data: prettyJsonData, encoding: .utf8) {
        print("jsonString", jsonString)
    }
    return prettyJsonData
}

func authResponse() {
    if let data = authData() {
        guard let response = try? JSONDecoder().decode(SEConfirmAuthorizationResponse.self, from: data) else {
            return
        }
        print("response", response)
    }
}