处理Response JSON数据结构的最佳方法?

问题描述 投票:3回答:1

我已经构建了一个后端并且正在使用前端客户端,目前,我的客户端的登录响应返回:

{
    "user": {
        "email": "[email protected]",
        "token": "eyJhbGciOiJIUzI"
    }
}

这给我一个问题,我不能简单地解码到用户对象,我必须为所有内容做以下2层:

struct User: Codable {
    let email: String
    let token: String
}

struct UserResponseData: Codable {
    let user: User
}

有没有更有效的方法来直接访问值并限制对象?也许我将user json的父母编辑为像data这样更通用的东西,然后将用户放在那里?我不确定......

我的客户端如下所示,如果这有助于通知改进的架构:

    class APIClient {

    static func signup(request: SignupRequestData, completion: @escaping (Result<UserResponseData>) -> Void) {
        performRequest(route: APIRouter.signup(request), completion: completion)
    }

    @discardableResult
    private static func performRequest<T:Decodable>(route: APIRouter,
                                                    decoder: JSONDecoder = JSONDecoder(),
                                                    completion:@escaping (Result<T>)->Void) -> DataRequest {
        return AF.request(route).responseDecodable (decoder: decoder){ (response: DataResponse<T>) in
            completion(response.result)
        }
    }
}

在一个更适合的结构中获得一些帮助,因此我不必继续展开父母以获得客户所需的价值

ios json alamofire
1个回答
2
投票

一种相当简单的方法是将其包装成通用的Response类型,假设第一个键始终是正确的。

struct AnyStringKey: CodingKey {
    var stringValue: String
    init?(stringValue: String) { self.stringValue = stringValue }
    var intValue: Int?
    init?(intValue: Int) { return nil }
}

struct Response<Payload: Decodable>: Decodable {
    let payload: Payload
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: AnyStringKey.self)
        guard let key = container.allKeys.first else {
            throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
                                                    debugDescription: "Missing payload key"))
        }
        self.payload = try container.decode(Payload.self, forKey: key)
    }
}

let user = try JSONDecoder().decode(Response<User>.self, from: json).payload

可以使这更高级,并检查密钥是否符合您的期望,但这可能足以满足您的需求。

这种方法将一些工作转移到调用者(调用.payload)。您可以通过使用处理提取子密钥的协议将一些工作转移到解码类型中来消除这种情况。

protocol LayerDecodable: Decodable {
    associatedtype CodingKeys: CodingKey
    init(from container: KeyedDecodingContainer<CodingKeys>) throws
}

extension LayerDecodable {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: AnyStringKey.self)
        guard let key = container.allKeys.first else {
            throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath,
                                                    debugDescription: "Missing payload key"))
        }
        try self.init(from: container.nestedContainer(keyedBy: CodingKeys.self, forKey: key))
    }
}

但有了这个,你需要手动实现解码。

struct User: LayerDecodable {
    let email: String
    let token: String

    enum CodingKeys: CodingKey {
        case email, token
    }

    init(from container: KeyedDecodingContainer<CodingKeys>) throws {
        self.email = try container.decode(String.self, forKey: .email)
        self.token = try container.decode(String.self, forKey: .token)
    }
}

好处是调用者现在是干净的:

let user = try JSONDecoder().decode(User.self, from: json)
© www.soinside.com 2019 - 2024. All rights reserved.