在 Swift 中使用 Alamofire 处理 XML 数据

Handling XML data with Alamofire in Swift

我开始在我当前的 ios 项目中使用 cocoapods。我需要使用 SOAP 以简单的方式为我的 ios 项目获取内容。我用谷歌搜索了一下,Alamofire pod 对我来说很棒。因为我使用的是Swift编程语言。

我已经轻松地启动了这个 pod。但是我的网络服务 return 我 XML 结果。我想序列化以排列此 XML 结果。但是我不能。

当我用浏览器调用我的网络服务时,我得到了这种结果

Alamofire的响应方式是这样的:

Alamofire.request(.GET, "http://my-web-service-domain.com", parameters: nil)
         .response { (request, response, data, error) in
                     println(request)
                     println(response)
                     println(error)
                   }

当我运行这个方法时,我在终端上看到这个输出:

<NSMutableURLRequest: 0x170010a30> { URL: http://my-web-service-domain.com }
Optional(<NSHTTPURLResponse: 0x1704276c0> { URL: http://my-web-service-domain.com } { status code: 200, headers {
    "Cache-Control" = "private, max-age=0";
    "Content-Length" = 1020;
    "Content-Type" = "text/xml; charset=utf-8";
    Date = "Thu, 18 Jun 2015 10:57:07 GMT";
    Server = "Microsoft-IIS/7.5";
    "X-AspNet-Version" = "2.0.50727";
    "X-Powered-By" = "ASP.NET";
} })
nil

我想得到一个数组的结果,该数组在浏览器上显示我的情节提要。 谁能帮助我如何使用 Alamofire 框架或 Swift 语言序列化这些数据?

如果我没有误解你的描述,我想你会想要得到 XML 数据并解析它,对吧?关于这一点,您可能会在响应回调中处理错误的变量。您应该 println(data) 查看 XML 文档。

解析XML数据,可以考虑SWXMLHash。 Alamofire 请求可能如下所示:

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

有关XML管理的更多信息,请查看SWXMLHash

使用截至 2015 年 9 月的 Alamofire 3.0 当前版本和 Xcode 7.

下面的实现的优点是不使用额外的外部库,例如 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
    }
  }
}

Alamofire 4.x - Swift 3.x:

(请注意,在这个例子中我使用了 URLEncoding.default 而不是 URLEncoding.xml 因为 xml 参数排除了传递参数和 headers 的可能性,所以default比较舒服。)

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 2015 年 10 月和 Xcode 7 根据 3.0.0-beta.3 README and the Alamofire 3.0 Migration Guide

对我来说正确的语法是:

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

如果你想要一个好的XML解析器,请看一看SWXMLHash

例如: let xml = SWXMLHash.parse(string)

我遇到了一个非常独特的问题,服务器返回了一个 XML,其中包含 JSON 作为字符串。希望对大家有帮助。

基本上 XML 看起来像这样:

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

如您所见,实际的 JSON 是 {"Response":....

该解决方案仅基于 Alamofire 4.4。

你需要做的是:

  1. 使用.responsePropertyList
  2. 检查错误
  3. 将值转换为数据
  4. 序列化为JSON对象
  5. 转换为字典 [String : Any]

这里是:

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

tsaiid 在Swift 3 和 Alamofire 4 中的回答:

Alamofire.request("http://my-web-service-domain.com", parameters: nil) //Alamofire defaults to GET requests
     .response { response in
        if let data = response.data {
          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.
        }
     }

如果你想把XML映射到swift对象,你也可以考虑XMLMapper. (uses the same technique as the ObjectMapper)

通过实施 XMLMappable 协议创建模型:

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

并使用 XMLMapper class 将 XML 字符串映射到模型对象中:

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

或者,您可以使用 Requests 子规范和 responseXMLObject(completionHandler:) 函数将响应直接映射到模型对象中:

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

希望有用。

如果你需要在 Alamofire 中使用 不同的解码器(JSON、URL、XML),最好和最简单的方法我发现正在使用 XMLCoder.

  • Alamofire 5
  • Swift 5

(它可能适用于旧版本)


在Alamofire的响应解码方法上,你只需要使用XMLDecoder()

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

像这样:

import XMLCoder

...

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

你的模型只需要符合 Codable 协议。

import Foundation

class ReportModel: Codable {
    var connections: ConnectionModel?
    var notifications: NotificationsModel?

    enum CodingKeys: String, CodingKey {
        case connections
        case notifications
    }
}

我创建了一个 GIST,其中包含如何使用 Alamofire 解码 XML 的完整结构,我添加了一些带有嵌套对象和 XML 属性的示例.