Alamofire 响应对象映射

Posted

技术标签:

【中文标题】Alamofire 响应对象映射【英文标题】:Alamofire response object mapping 【发布时间】:2017-11-03 04:23:40 【问题描述】:

我是 swift 3 编程新手,我正在使用 Alamofire 进行 api 调用并避免繁琐的 json 配对,我正在使用 AlamofireObjectMapper 库。 我有一个ApiController,它有一个调用api的函数,下面是它的代码:

public static func makePostRequest<T: Mappable>(url: String, params: Parameters, networkProtocol: NetworkProtocol, responseClass: T)

    let headers = getHeaders()

    networkProtocol.showProgress()

    Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
        .validate()
        .responseData response in
            let json = response.result.value
            var jsonString = String(data: json!, encoding: String.Encoding.utf8)
            let responseObject = responseClass(JSONString: jsonString!)
            switch(response.result)
            case .success(_):
                networkProtocol.hideProgress()
                networkProtocol.onResponse(response: response)
                break
            case .failure(_):
                networkProtocol.hideProgress()
                networkProtocol.onErrorResponse(response: response)
                break
            

    

我从服务器获取的 Json 响应模板是:


 "some_int": 10, 
 "some_array":[...]

下面是我的模型类:

import ObjectMapper

    class BaseResponse: Mappable 
    var some_int: Int?
    var some_array: [Array_of_objects]?

    required init?(map: Map) 
        some_int <- map["some_int"]
        some_array <- map["some_array"]
    

    func mapping(map: Map) 

    

下面是类调用 api 的函数:

public static func callSomeApi(params: Parameters, networkProtocol: NetworkProtocol)
    ApiHelper.makePostRequest(url: AppConstants.URLs.API_NAME, params: params, networkProtocol: networkProtocol, responseClass: BaseResponse)

现在错误在下面一行

let responseObject = responseClass(JSONString: jsonString!)

我无法理解如何将 jsonString 转换为我从视图控制器接受的 responseClass 通用对象

有人请帮我解决这个问题,现在卡在这个问题上很长一段时间了。

【问题讨论】:

【参考方案1】:

您可以使用 SwiftyJSON:https://cocoapods.org/pods/SwiftyJSON

这里有一些示例代码可以帮助你:

Alamofire.request(endpointURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: nil).validate().responseJSON()
 
        (response) in

        if response.result.isFailure
        
            print("ERROR! Reverse geocoding failed!")
        
        else if let value = response.result.value
        
           var country: String? = nil
           var county: String? = nil
           var city: String? = nil
           var town: String? = nil
           var village: String? = nil

           print("data: \(value)")

           let json = JSON(value)

           print("json: \(json)")

           country = json["address"]["country"].string
           county = json["address"]["county"].string
           city = json["address"]["city"].string
           town = json["address"]["town"].string
           village = json["address"]["village"].string
        
        else
        
            print("Cannot get response result value!")
        

 

请让您知道代码已被简化(很多行已被删除)并从我的实际项目中粘贴到此处,因此此代码未经测试,可能包含拼写错误或类似内容,但逻辑是可见

【讨论】:

感谢您的建议,我的问题是我想将响应保存在 SwiftyJson 无法帮助的 BaseResponse 模型类中。 好的,很抱歉给我带来了误解!【参考方案2】:

对于对象映射,您需要遵循 AlamofireObjectMapper

//Declare this before ViewLoad
var BaseResponse: Array<BaseResponse>?

// After you receive response  from API lets say "data"
 if let jsonData = data as? String 
      self.BaseResponse = Mapper< BaseResponse >().mapArray(JSONString: jsonData)

【讨论】:

响应不是一个数组,它只是一个 json 对象。 "some_int": 10 "some_array":[...] 这是我从服务器获得的 Json 响应模板【参考方案3】:

使用 Swift 4 Codable 的通用响应对象序列化

如果您不想使用像 ObjectMapper 这样的其他依赖项,您可以执行以下方式,但您可能需要进行一些更改。


Following 是我们使用 Alamofire 使用泛型反序列化 JSON 数据的典型模型。 Alamofire 上有很多例子和优秀的documentation。

struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible 
    let username: String
    let name: String

    var description: String 
        return "User:  username: \(username), name: \(name) "
    

    init?(response: HTTPURLResponse, representation: Any) 
        guard
            let username = response.url?.lastPathComponent,
            let representation = representation as? [String: Any],
            let name = representation["name"] as? String
        else  return nil 

        self.username = username
        self.name = name
    

使用 Swift 4 中引入的 Codable 协议

typealias Codable = Decodable & Encodable

朝着这个方向迈出的第一步是添加帮助函数 将在反序列化 JSON 数据和处理 错误。使用 Swift 扩展,我们添加了解码传入的函数 JSON 到我们将在之后编写的模型结构/类中。

let decoder = JSONDecoder()
let responseObject = try? decoder.decode(T.self, from: jsonData)
The decoder (1) is an object that decodes instances of a data type from JSON objects.

辅助函数

extension DataRequest
    /// @Returns - DataRequest
    /// completionHandler handles JSON Object T
    @discardableResult func responseObject<T: Decodable> (
        queue: DispatchQueue? = nil ,
        completionHandler: @escaping (DataResponse<T>) -> Void ) -> Self

        let responseSerializer = DataResponseSerializer<T>  request, response, data, error in
            guard error == nil else return .failure(BackendError.network(error: error!))

            let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
            guard case let .success(jsonData) = result else
                return .failure(BackendError.jsonSerialization(error: result.error!))
            

            // (1)- Json Decoder. Decodes the data object into expected type T
            // throws error when failes
            let decoder = JSONDecoder()
            guard let responseObject = try? decoder.decode(T.self, from: jsonData)else
                return .failure(BackendError.objectSerialization(reason: "JSON object could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
            
            return .success(responseObject)
        
        return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
    

     /// @Returns - DataRequest
    /// completionHandler handles JSON Array [T]
    @discardableResult func responseCollection<T: Decodable>(
        queue: DispatchQueue? = nil, completionHandler: @escaping (DataResponse<[T]>) -> Void
        ) -> Self

        let responseSerializer = DataResponseSerializer<[T]> request, response, data, error in
            guard error == nil else return .failure(BackendError.network(error: error!))

            let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
            guard case let .success(jsonData) = result else
                return .failure(BackendError.jsonSerialization(error: result.error!))
            

            let decoder = JSONDecoder()
            guard let responseArray = try? decoder.decode([T].self, from: jsonData)else
                return .failure(BackendError.objectSerialization(reason: "JSON array could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
            

            return .success(responseArray)
        
        return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
    
 

第二,我之前提到过“使用 Swift 4 Codable”,但如果我们都 想要的是从服务器解码JSON,我们只需要一个模型 符合可解码协议的结构/类。 (如果你有 您要上传的相同结构可以使用 Codable 来处理 解码和编码)所以,现在我们的用户模型结构现在看起来像 这个。

struct User: Decodable, CustomStringConvertible 
    let username: String
    let name: String

    /// This is the key part
    /// If parameters and variable name differ
    /// you can specify custom key for mapping "eg. 'user_name'"

    enum CodingKeys: String, CodingKey 
        case username = "user_name"
        case name
    

    var description: String 
        return "User:  username: \(username), name: \(name) "
    


最后,我们对 API 的函数调用如下所示。

Alamofire.request(Router.readUser("mattt"))).responseObject (response: DataResponse<User>) in

            // Process userResponse, of type DataResponse<User>:
            if let user = response.value 
                print("User:  username: \(user.username), name: \(user.name) ")
            


对于更复杂(嵌套)的 JSON,逻辑保持不变,您需要在模型结构/类中进行的唯一修改是所有结构/类必须符合 Decodable 协议,而 Swift 会处理其他所有事情。

【讨论】:

【参考方案4】:

你可以使用 AlamofireMapper:

带json:


   "page":1,
   "per_page":3,
   "total":12,
   "total_pages":4,
   "data":[
      
         "id":1,
         "first_name":"George",
         "last_name":"Bluth",
         "avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
      ,
      
         "id":2,
         "first_name":"Janet",
         "last_name":"Weaver",
         "avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
      ,
      
         "id":3,
         "first_name":"Emma",
         "last_name":"Wong",
         "avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
      
   ]

Swift 类:

class UserResponse: Decodable 
    var page: Int!
    var per_page: Int!
    var total: Int!
    var total_pages: Int!

    var data: [User]?


class User: Decodable 
    var id: Double!
    var first_name: String!
    var last_name: String!
    var avatar: String!

使用 alamofire 请求

let url1 = "https://raw.githubusercontent.com/sua8051/AlamofireMapper/master/user1.json"
        Alamofire.request(url1, method: .get
            , parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject  (response: DataResponse<UserResponse>) in
                switch response.result 
                case let .success(data):
                    dump(data)
                case let .failure(error):
                    dump(error)
                
        

链接:https://github.com/sua8051/AlamofireMapper

【讨论】:

以上是关于Alamofire 响应对象映射的主要内容,如果未能解决你的问题,请参考以下文章

从 JSON 响应创建字符串数组 Alamofire

使用 Alamofire 和不记名令牌序列化对象

尝试将 AlamoFire JSON 响应映射到类并接收“致命错误:在展开可选时意外发现 nil”

在 AlamoFire 框架中访问映射对象返回 nil 值

Swift - 嵌套对象的映射 (Objectmapper)

Alamofire,Objectmapper,Realm:嵌套对象