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

标签 ios json alamofire

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

{
    "user": {
        "email": "fdsa@fdsafsdfa.com",
        "token": "eyJhbGciOiJIUzI"
    }
}

这给我带来了一个问题,我无法简单地解码为用户对象,我必须为所有内容执行以下 2 层:

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

struct UserResponseData: Codable {
    let user: User
}

是否有更有效的方法来直接访问值并限制对象?也许我将 user json parent 编辑为更通用的东西,比如 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)
        }
    }
}

感谢在更适合的结构中提供的一些帮助,这样我就不必继续解开父级来获得客户所需的值

最佳答案

一个相当简单的方法是将其包装到一个通用的 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

可以使其更高级,并检查 key 是否符合您的预期,但这可能足以满足您的情况。

这种方法将一些工作转移到调用方(调用 .payload)。您可以通过使用处理提取子 key 的协议(protocol)将一些工作转移到解码类型中来摆脱它。

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)

关于ios - 处理 Response JSON 数据结构的最佳方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55344959/

相关文章:

objective-c - 如何将获取的对象保存到核心数据

ios - 卸载应用程序后注销用户 - Firebase

java - 谷歌地图 getdirections api - 折线

java - 从 JSONArray 获取 JSONObject

ios - 从 SWIFT 中的 API 缓存 JSON 的最佳方法?

ios - 无法加载 UITests 包,因为它已损坏或缺少必要的资源。尝试重新安装 bundle

ios - 滚动后 CollectionView 未填充

php - 数据未从 Python 传递到 PHP

json - 处理多页 JSON 和 TableView?

ios - 带有 UITableView 单元格的 Swift 4 AlamofireImage