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

时间:2019-03-25 19:15:12

标签: ios json alamofire

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

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

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

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

在向前发展的更合适的结构中感谢一些帮助,因此我不必不断地解开父级来获得客户所需的值

1 个答案:

答案 0 :(得分: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)