Handling XML data with Alamofire in Swift

后端 未结 7 1939
一向
一向 2020-12-24 02:57

I started to use cocoapods with my current ios project. I need to use SOAP to get content with easy way for my ios project. I have googled it and Alamofire pod is great for

相关标签:
7条回答
  • 2020-12-24 03:09

    Using Alamofire 3.0 current version as of Sept 2015 and Xcode 7.

    The implementation bellow has the advantage of not using an additional external library such as SWXMLHash

    Alamofire.request(.GET, urlString, encoding: .PropertyList(.XMLFormat_v1_0, 0)).responsePropertyList { request, response, result in
    
    //Note that result have two properties: error and value as of Alamofire 3.0, check the migration guide for more info
    
      if let error = result.error {
        print("Error: \(error)")
    
        // parsing the data to an array 
      } else if let array = result.value as? [[String: String]] {
    
        if array.isEmpty {
          print("No data")
    
        } else { 
          //Do whatever you want to do with the array here
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-24 03:13

    If I did not misunderstand your description, I think you would like to get the XML data and parse it, right? Regarding to this, you may handle with wrong variables in the response callback. You should println(data) to check the XML document.

    For parsing XML data, you could consider SWXMLHash. The Alamofire request could look like:

    Alamofire.request(.GET, "http://my-web-service-domain.com", parameters: nil)
             .response { (request, response, data, error) in
                println(data) // if you want to check XML data in debug window.
                var xml = SWXMLHash.parse(data!)
                println(xml["UserDTO"]["FilmID"].element?.text) // output the FilmID element.
             }
    

    Further information about XML management, please check SWXMLHash.

    0 讨论(0)
  • 2020-12-24 03:13

    I had a really unique issue where the server returned an XML that has a JSON as a string. Hope it will help someone.

    Basically the XML looked like this:

    <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">{"Response":{"Status":"success","Result_Count":"1","Error_Description":"","Result":{"Login_result":{"user_id":"1","user_type":"1","user_name":"h4cked","user_locked":"False","user_locked_message":""}}}}</string>
    

    As you can see the actual JSON is the {"Response":....

    The solution is based only on Alamofire 4.4.

    What you need to do is this:

    1. Use the .responsePropertyList
    2. Check for error
    3. Convert the value to Data
    4. Serialize to JSON object
    5. Cast to Dictionary [String : Any]

    Here it is:

    Alamofire.request(NetworkAPIPaths.pathForLogin(),
                          method: .get,
                          parameters: [APIParameters.userName.rawValue : "",
                                       APIParameters.password.rawValue : ""]).responsePropertyList
            { (response : DataResponse<Any>) in
    
        if let error = response.result.error
        {
            // Error...
        }
        else if let jsonFullString = response.result.value as? String
        {
            if let jsonStringAsData = jsonFullString.data(using: .utf8)
            {
                do
                {
                    let jsonGhost = try JSONSerialization.jsonObject(with: jsonStringAsData, options: [])
    
                    if let actualJSON = jsonGhost as? [String : Any]
                    {
                       // actualJSON is ready to be parsed :)
                    }
                 }
                 catch
                 {
                   print (error.localizedDescription)
                 }
            }
        }
    
    0 讨论(0)
  • 2020-12-24 03:13

    If you need to use different decoders (JSON, URL, XML) with Alamofire, the best and simplest way I found was using XMLCoder.

    • Alamofire 5
    • Swift 5

    (It will probably work on older versions)


    On the Alamofire's response decodable method, you just need to use XMLDecoder()

    @discardableResult
    public func responseDecodable<T: Decodable>(of type: T.Type = T.self,
                                                ...
                                                decoder: DataDecoder = JSONDecoder(),
                                                ...
                                                completionHandler: @escaping (AFDataResponse<T>) -> Void) -> Self {
    

    Something like this:

    import XMLCoder
    
    ...
    
    dataRequest
        .validate()
        .responseDecodable(of: T.self, decoder: XMLDecoder()) { (response: DataResponse<T, AFError>) in
            
    

    And your models just need to conform the Codable protocol.

    import Foundation
    
    class ReportModel: Codable {
        var connections: ConnectionModel?
        var notifications: NotificationsModel?
    
        enum CodingKeys: String, CodingKey {
            case connections
            case notifications
        }
    }
    

    I created a GIST with a complete structure of how you can decode XML using Alamofire and I added some examples with nested objects and XML attributes.

    • https://gist.github.com/Enriquecm/bedf024d8a8f519eb1185fef14adcec3

    0 讨论(0)
  • 2020-12-24 03:15

    If you want to map the XML to swift objects, you may also consider XMLMapper. (uses the same technique as the ObjectMapper)

    Create your model by implementing XMLMappable protocol:

    class UserDTO: XMLMappable {
        var nodeName: String!
    
        var extensionData: String?
        var canChangeDeviceConfig: BooleanAtttribute?
        var canChangeDriverConfig: BooleanAtttribute?
        var canChangeFleetConfig: BooleanAtttribute?
        var canChangeGeofenceConfig: BooleanAtttribute?
        var canSaveHistory: BooleanAtttribute?
        var canViewReport: BooleanAtttribute?
        var canWatchHistory: BooleanAtttribute?
        var deliverDailyReportByEmail: BooleanAtttribute?
        var deliverDailyReportBySms: BooleanAtttribute?
        var email: String?
        var firm: String?
        var firmId: Int?
        var firstName: String?
        var id: Int?
        var isActive: Bool?
        var isAdmin: Bool?
        var lastName: String?
        var phone: String?
        var recivesDailyReport: BooleanAtttribute?
        var userName: String?
    
        required init(map: XMLMap) {
    
        }
    
        func mapping(map: XMLMap) {
            extensionData <- map["ExtensionData"]
            canChangeDeviceConfig <- map["CanChangeDeviceConfig"]
            canChangeDriverConfig <- map["CanChangeDriverConfig"]
            canChangeFleetConfig <- map["CanChangeFleetConfig"]
            canChangeGeofenceConfig <- map["CanChangeGeofenceConfig"]
            canSaveHistory <- map["CanSaveHistory"]
            canViewReport <- map["CanViewReport"]
            canWatchHistory <- map["CanWatchHistory"]
            deliverDailyReportByEmail <- map["DeliverDailyReportByEmail"]
            deliverDailyReportBySms <- map["DeliverDailyReportBySms"]
            email <- map["Email"]
            firm <- map["Firm"]
            firmId <- map["FirmId"]
            firstName <- map["FirstName"]
            id <- map["Id"]
            isActive <- (map["IsActive"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
            isAdmin <- (map["IsAdmin"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
            lastName <- map["LastName"]
            phone <- map["Phone"]
            recivesDailyReport <- map["RecivesDailyReport"]
            userName <- map["UserName"]
        }
    }
    
    class BooleanAtttribute: XMLMappable {
        var nodeName: String!
    
        var booleanValue: Bool?
    
        required init(map: XMLMap) {
    
        }
    
        func mapping(map: XMLMap) {
            booleanValue <- (map.attributes["xsi:nil"], BooleanTransformeType(trueValue: "true", falseValue: "false"))
        }
    }
    
    class Firm: XMLMappable {
        var nodeName: String!
    
        var extensionData: String?
        var firmTypeId: Int?
        var id: Int?
        var name: String?
        var parentFirmId: Int?
    
        required init(map: XMLMap) {
    
        }
    
        func mapping(map: XMLMap) {
            extensionData <- map["ExtensionData"]
            firmTypeId <- map["FirmTypeId"]
            id <- map["Id"]
            name <- map["Name"]
            parentFirmId <- map["ParentFirmId"]
        }
    }
    
    class BooleanTransformeType<T: Equatable>: XMLTransformType {
        typealias Object = Bool
        typealias XML = T
    
        private var trueValue: T
        private var falseValue: T
    
        init(trueValue: T, falseValue: T) {
            self.trueValue = trueValue
            self.falseValue = falseValue
        }
    
        func transformFromXML(_ value: Any?) -> Bool? {
            if let value = value as? T {
                return value == trueValue
            }
            return nil
        }
    
        func transformToXML(_ value: Bool?) -> T? {
            if value == true {
                return trueValue
            }
            return falseValue
        }
    }
    

    And use the XMLMapper class to map the XML string into the model objects:

    let userDTO = XMLMapper<UserDTO>().map(XMLString: xmlString)
    

    Altenatively you can use Requests subspec and the responseXMLObject(completionHandler:) function to map the response directly into the model objects:

    Alamofire.request("http://my-web-service-domain.com", method: .get).responseXMLObject { (response: DataResponse<UserDTO>) in
        let userDTO = response.result.value
        print(userDTO?.id ?? "nil")
    }
    

    I hope this is useful.

    0 讨论(0)
  • 2020-12-24 03:29

    Alamofire 4.x - Swift 3.x:

    (please note that in this example I've used URLEncoding.default instead of URLEncoding.xml because the xml parameter exclude the possibility to pass parameters and headers, so default is more confortable.)

    let url = "https://httpbin.org/get"
    let parameters: Parameters = ["foo": "bar"]
    let headers: HTTPHeaders = [
        "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
        "Accept": "application/json"
    ]
    Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers)
    .responseString { response in
        print(" - API url: \(String(describing: response.request!))")   // original url request
        var statusCode = response.response?.statusCode
    
        switch response.result {
        case .success:
            print("status code is: \(String(describing: statusCode))")
            if let string = response.result.value {
                print("XML: \(string)")
            }
        case .failure(let error):
            statusCode = error._code // statusCode private
            print("status code is: \(String(describing: statusCode))")
            print(error)
        }
    }
    

    Alamofire 3.0 october 2015 and Xcode 7 according to the 3.0.0-beta.3 README and the Alamofire 3.0 Migration Guide.

    For me the correct syntax is:

    Alamofire.request(.GET, url, parameters: params, encoding: ParameterEncoding.URL).responsePropertyList { response in
    
                if let error = response.result.error {
                    print("Error: \(error)")
    
                    // parsing the data to an array
                } else if let array = response.result.value as? [[String: String]] {
    
                    if array.isEmpty {
                        print("No data")
    
                    } else { 
                        //Do whatever you want to do with the array here
                    }
                }
            }
    

    If you want a good XML parser, please take a look to SWXMLHash.

    An example could be: let xml = SWXMLHash.parse(string)

    0 讨论(0)
提交回复
热议问题