如何在 iOS Swift 中获取 XML 数据并在 tableview 中绑定?

Posted

技术标签:

【中文标题】如何在 iOS Swift 中获取 XML 数据并在 tableview 中绑定?【英文标题】:How to get XML data and bind in tableview in iOS Swift? 【发布时间】:2019-08-16 20:26:08 【问题描述】:

我一直在使用具有基本授权的 oData url。 oXML 格式的数据响应。我可以使用 Alamofire 从 oData url 响应数据。但我无法从 XML 中获取特定值。

这是我的 XML 响应数据:

 <feed xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xml:base="http://hostname:portNumber/sap/opu/odata/sap/ZPRJ_PM_APPS_IH_SRV/">
<id>hostname:portNumber/sap/opu/odata/sap/ZPRJ_PM_APPS_IH_SRV/WorkOrder</id>
<title type="text">WorkOrderF4Set</title>
<updated>2019-08-16T15:37:48Z</updated>
<author>
    <name/>
</author>
<link href="WorkOrderF4Set" rel="self" title="WorkOrderF4Set"/>
<entry>
    <id>hostname:portNumber/sap/opu/odata/sap/ZPRJ_PM_APPS_IH_SRV/WorkOrder('000000504780')</id>
    <title type="text">WorkOrder('000000504780')</title>
    <updated>2019-08-16T15:37:48Z</updated>
    <category term="ZPRJ_PM_APPS_IH_SRV.WorkOrder" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
    <link href="WorkOrder('000000504780')" rel="self" title="WorkOrder"/>
    <content type="application/xml">
        <m:properties xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
            <d:WorkOrder>000000504780</d:WorkOrder>
            <d:Description>General Maintenance testing</d:Description>
        </m:properties>
    </content>
</entry>
<entry>
    <id>http://hostname:portNumber/sap/opu/odata/sap/ZPRJ_PM_APPS_IH_SRV/WorkOrder('000000821400')</id>
    <title type="text">WorkOrderF4Set('000000821400')</title>
    <updated>2019-08-16T15:37:48Z</updated>
    <category term="ZPRJ_PM_APPS_IH_SRV.WorkOrder" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"/>
    <link href="WorkOrder('000000821400')" rel="self" title="WorkOrder"/>
    <content type="application/xml">
        <m:properties xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">
            <d:WorkOrder>000000821400</d:WorkOrder>
            <d:Description>PUMP LEAKING</d:Description>
        </m:properties>
    </content>
</entry>
</feed> 

我在这里尝试获取 d:WorkOrderd:Description 值并绑定到表格视图单元格中。

这是我从 alamofire 获得响应的代码:

  func loadData()



    let user = "raXXXXXX"

    let password = "xxxxxxx"

    let url = "http://hostname:portNumber/sap/opu/odata/sap/ZPRJ_PM_APPS_IH_SRV/WorkOrder"



    let loginString = "\(user):\(password)"





    guard let loginData = loginString.data(using: String.Encoding.utf8) else 

        return

    

    let base64LoginString = loginData.base64EncodedString()

    print("base 64 login :\(base64LoginString)")



    let headers = ["Authorization": "Basic \(base64LoginString)"]


    Alamofire.request(url, method: .get, 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 stringg = response.result.value 

                    print("XML: \(stringg)")



                    let data = stringg.data(using: .utf8)



                    let xml = try! XML.parse(data!)

                    print("xml value:\(xml)")



                    let element = xml.feed.entry

                    print("element xml :\(element)")



                    let results = xml["feed", "entry"]

                    print("result xml :\(results)")





                    for result in results["entry"] 

                        if let jobtitle = result["d:WorkOrder"].text 

                            print("job::\(jobtitle)")

                        

                    

                    if let attributeValue = xml.feed.entry[0].attributes["d:WorkOrder"] 

                        print("attributeValue\(attributeValue)") // -> 2

                    



                    let work = "d:WorkOrder"

                    print("feed:\(xml.feed.entry[0].WorkOrder)")



                    guard let jsonData = stringg.data(using: .utf8) else return

                    guard let jsonResponse = (try? JSONSerialization.jsonObject(with: jsonData)) as? [[String:Any]] else return

                    let idArray = jsonResponse.flatMap$0["d:WorkOrder"] as? String



                    print("idarray::\(idArray)")

                


                if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) 

                    print("Data: \(utf8Text)") // original server data as UTF8 string

                

            case .failure(let error):

                statusCode = error._code // statusCode private

                print("status code is: \(String(describing: statusCode))")

                print(error)

            

    


//loaddata

也尝试使用 XMLSwiftyParser pod,但效果不佳。

  let data = stringg.data(using: .utf8)



                    let xml = try! XML.parse(data!)

                    print("xml value:\(xml)")



                    let element = xml.feed.entry

                    print("element xml :\(element)")



                    let results = xml["feed", "entry"]

                    print("result xml :\(results)")





                    for result in results["entry"] 

                        if let jobtitle = result["d:WorkOrder"].text 

                            print("job::\(jobtitle)")

                        

                    

如何在表格视图中解析 d:WorkOrder 和 d:Description。

任何帮助都非常感谢...

【问题讨论】:

【参考方案1】:

您可以使用此代码解析任何 xml 数据。然后您可以访问 parsedData 变量中的数据。使用辅助功能“搜索”,您可以获得任意键的任意值。

class ViewController: UIViewController, XMLParserDelegate 

    var parsedData = Dictionary<String,Any>()
    var openElements = [String]()

    override func viewDidLoad() 
        super.viewDidLoad()

        if let path = Bundle.main.url(forResource: "Feed", withExtension: "xml") 
            if let parser = XMLParser(contentsOf: path) 
                parser.delegate = self
                parser.parse()
            
        
    

    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) 
        openElements.append(elementName)

        parsedData[elementName] = attributeDict
    

    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) 
        _ = openElements.popLast()

        if let last = openElements.last, var dict = parsedData[last] as? [String:Any], let data = parsedData[elementName] 
            if let prevData = dict[elementName] 
                dict[elementName] = [prevData, data]
             else 
                dict[elementName] = data
            

            parsedData[last] = dict

            parsedData.removeValue(forKey: elementName)
        
    

    func parser(_ parser: XMLParser, foundCharacters string: String) 
        let string = string.trimmingCharacters(in: .whitespacesAndNewlines)
        if !string.isEmpty, let last = openElements.last, var dict = parsedData[last] as? [String:Any] 
            dict["text"] = string

            parsedData[last] = dict
        
    

    func parserDidEndDocument(_ parser: XMLParser) 
        search(key: "d:WorkOrder", in: parsedData, completion:  print($0) )
        search(key: "d:Description", in: parsedData, completion:  print($0) )
    

    func search(key:String, in dict:[String:Any], completion:((Any) -> ())) 
        if let foundValue = dict[key] 
            completion(foundValue)
         else 
            dict.values.enumerated().forEach 
                if let innerDict = $0.element as? [String:Any] 
                    search(key: key, in: innerDict, completion: completion)
                

                if let innerArray = $0.element as? [[String:Any]] 
                    innerArray.forEach( innerDict in
                        search(key: key, in: innerDict, completion: completion)
                    )
                
            
        
    

【讨论】:

如何在 tableview 中使用效果很好。我需要在 tableview 中加载 d:WorkOrder 和 d:Description【参考方案2】:

基于@Hamza Öztürk 的回答,尝试使用 XMLParse:

 import UIKit

 struct Book 
   var workOrder: String
   var description: String
 

 class TableViewController: UITableViewController, XMLParserDelegate 

var books: [Book] = []
var elementName: String = String()
var bookWorkOrder = String()
var bookdescription = String()


override func viewDidLoad() 
    super.viewDidLoad()



    if let path = Bundle.main.url(forResource: "Feed", withExtension: "xml") 
        if let parser = XMLParser(contentsOf: path) 
            parser.delegate = self
            parser.parse()
        
     
   

   // 1
   func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) 

    if elementName == "entry" 
        bookWorkOrder = String()
        bookdescription = String()
    

    self.elementName = elementName

    

    // 2
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) 
    if elementName == "entry" 
        let book = Book(workOrder: bookWorkOrder, description: bookdescription)
        books.append(book)
        print("count::\(books.count)")


       
   

   // 3
   func parser(_ parser: XMLParser, foundCharacters string: String) 
    let data = string.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)

    if (!data.isEmpty) 
        if self.elementName == "d:WorkOrder" 
            bookWorkOrder += data
         else if self.elementName == "d:Description" 
            bookdescription += data
        
     
   



   // MARK: - Table view data source
    override func numberOfSections(in tableView: UITableView) -> Int 
    // return the number of sections
    return 1
   

      override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
    // return the number of rows

    return books.count
   


      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
      let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! cellTableViewCell

    let book = books[indexPath.row]
    print("book::\(book.workOrder)")

    cell.workorfer.text = book.workOrder
    cell.descriptor.text = book.description


    return cell
   





【讨论】:

【参考方案3】:

你也可以试试XMLMapper pod。

使用嵌套映射功能,您的模型可能如下所示:

class Feed: XMLMappable 
    var nodeName: String!

    var entries: [Entry]?

    required init?(map: XMLMap) 

    func mapping(map: XMLMap) 
        entries <- map["entry"]
    


class Entry: XMLMappable 
    var nodeName: String!

    var workOrder: String?
    var description: String?

    required init?(map: XMLMap) 

    func mapping(map: XMLMap) 
        workOrder <- map["content.m:properties.d:WorkOrder"]
        description <- map["content.m:properties.d:Description"]
    

您可以将您的 XML 映射到此模型,例如:

let feed = Feed(XMLString: xmlString)

或使用Requests 子规范:

Alamofire.request(url, method: .get, encoding: URLEncoding.default, headers: headers)
    .responseXMLObject  (dataResponse: DataResponse<Feed>) in
        let feed = dataResponse.result.value
        print(feed?.toXMLString() ?? "nil")

【讨论】:

以上是关于如何在 iOS Swift 中获取 XML 数据并在 tableview 中绑定?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 iOS Swift 中过滤 JSON 并获取价值?

从 api 获取所有数据以及如何在 swift ios 中仅在单元格上附加标题

使用 alamofire 和 Swift4 IOS 获取 XML HTTP url 的响应

如何在IOS APP中通过swift/javascript登录网站

如何在 Swift iOS 中读取和记录图像的原始像素

如何在 swift iOS 中使用 swift 高阶函数从本地 json 创建 ViewModel