2015-10-15 51 views
0

我想解析Swift中的XML文件,并且在理解NSMutableDictionary如何在存储数值的过程中遇到一些麻烦元素。解析Swift中的XML文件(Xcode v 7.0.1)并从字典中检索值

的XML文件看起来像这样,

<coord2 count="6"> 
    <markers> 
    <marker> 
     <lat>36.99058167</lat> 
     <lng>-122.06620333</lng> 
     <timestamp>1444931620</timestamp> 
     <route>LOOP</route> 
     <id>7855</id> 
     <predictions> 
     ,4,5,,6,7,,,,10,,,11,,11,,13,15,,14,,,16,,,0,,,2,,,,,,,,,,,,,, 
     </predictions> 
     <update_seconds>10</update_seconds> 
     <index>4</index> 
    </marker> 
    <marker> 
     <lat>36.99296</lat> 
     <lng>-122.06517333</lng> 
     <timestamp>1444934786</timestamp> 
     <route>UPPER CAMPUS</route> 
     <id>7860</id> 
     <predictions> 
     15,,,14,,,13,12,11,,10,9,,8,,7,,,6,,5,4,,3,2,,1,0,,,,,,,,,,,,,,, 
     </predictions> 
     <update_seconds>10</update_seconds> 
     <index>4</index> 
    </marker> 
    </markers> 
    <curr_time>1444931622</curr_time> 
</coord2> 

我试图解析XML文件的方法是使用下面的代码,我从一个教程发现在 http://www.theappguruz.com/blog/xml-parsing-using-nsxmlparse-swift

import UIKit 
import GoogleMaps 

class ViewController: UIViewController, NSXMLParserDelegate { 

    var parser = NSXMLParser() 
    var posts = NSMutableArray() 
    var elements = NSMutableDictionary() 
    var element = NSString() 
    var route = NSMutableString() 
    var timestamp = NSMutableString() 

    func beginParsing() 
    { 
     posts = [] 
     parser = NSXMLParser(contentsOfURL:(NSURL(string:"http://xmfile.xml"))!)! 
     parser.delegate = self 
     parser.parse() 
    } 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     beginParsing() 

     print("The route is") 
     let t = elements["route"]! 
     print(t) 
     print("The timestamp is") 
     let u = elements["timestamp"]! 
     print(u) 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 

    // didStartElement 
    func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) 
    { 
     element = elementName 
     if (elementName as NSString).isEqualToString("marker") 
     { 
      elements = NSMutableDictionary() 
      elements = [:] 
      route = NSMutableString() 
      route = "" 
      timestamp = NSMutableString() 
      timestamp = "" 
     } 
    } 

    // foundCharacters 
    func parser(parser: NSXMLParser, foundCharacters string: String) 
    { 
     if element.isEqualToString("route") { 
      route.appendString(string) 
     } else if element.isEqualToString("timestamp") { 
      timestamp.appendString(string) 
     } 
    } 

    // didEndElement 
    func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) 
    { 
     if (elementName as NSString).isEqualToString("marker") { 
      if !route.isEqual(nil) { 
       elements.setObject(route, forKey: "route") 
      } 
      if !timestamp.isEqual(nil) { 
       elements.setObject(timestamp, forKey: "timestamp") 
      } 
      posts.addObject(elements) 
     } 
    } 

} 

现在我只关注“路由”和“时间戳”元素,以便了解分析的工作原理。输出我得到的,当我

let t = elements["route"]! 
print(t) 
print("previous is route") 
let u = elements["timestamp"]! 
print(u) 
print("previous is timestamp") 

始终是在XML文件中的名称最后元素的值。所以,对于我提供的6行的代码的输出处的示例XML文件上面会

路线是
UPPER校园

该时间戳为

我希望能够区分多个“”标记“elem浏览XML文件并检索标记内元素的值。换句话说,能够获得第一个标记内元素的值,而不仅仅是最后一个。我会如何去做这件事?

+0

你是否需要使用NSMutableDictionary/NSMutableArray或创建类来表示Coord2/Marker是一个选项?我想问的原因是,如果你可以使用自定义类,你可以使用另一种方法来干净地解析你的XML,并有更多可管理的代码... – Benzi

+0

感谢您的回应。现在,我接受建议。我对Swift很陌生,所以我在学习。我如何用自定义类来完成这个任务? – Ivan

+0

好的...我已经为你提供了一个答案;请在下面查看。 – Benzi

回答

4

注意 我已经把whole thing in a gist您可以复制并粘贴到一个游乐场。


让我们来看一个简单的例子来获得一个开始:

let xml = "<coord2 count=\"3\">" 
+ "<markers>" 
    + "<marker>" 
    + "<item>marker1</item>" 
    + "</marker>" 
    + "<marker>" 
    + "<item>marker2</item>" 
    + "<lat>36</lat>" 
    + "</marker>" 
+ "</markers>" 
+ "</coord2>" 

有点收窄,但标记可以有一个项目名称(字符串)和LAT值(INT)。一个Coord2将有一个标记数组和一个count(int)属性。

要使用自定义类来解析上述问题,下面介绍一种方法。

首先创建一个ParserBase类,为我们做一些基础工作,即积累foundCharacters,以便它可以很容易地被子类使用。 此外(更重要的)它有一个parent属性,用于保存对父容器类的引用[这用于我们将解析XML的方式]。

// Simple base class that is used to consume foundCharacters 
// via the parser 

class ParserBase : NSObject, NSXMLParserDelegate { 

    var currentElement:String = "" 
    var foundCharacters = "" 
    weak var parent:ParserBase? = nil 

    func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { 

     currentElement = elementName 
    } 

    func parser(parser: NSXMLParser, foundCharacters string: String) { 
     self.foundCharacters += string 
    } 

} 

由于coord2是我们的根标签,我们将创建一个类,将映射到该标签 - 它代表根对象,有标记,计数属性的数组,也是根代表XMLParser的对象。

// Represents a coord2 tag 
// It has a count attribute 
// and a collection of markers 

class Coord2 : ParserBase { 


    var count = 0 
    var markers = [Marker]() 



    override func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { 

     print("processing <\(elementName)> tag from Coord") 

     if elementName == "coord2" { 

      // if we are processing a coord2 tag, we are at the root 
      // of this example 
      // extract the count value and set it 
      if let c = Int(attributeDict["count"]!) { 
       self.count = c 
      } 
     } 

     // if we found a marker tag, delegate further responsibility 
     // to parsing to a new instance of Marker 

     if elementName == "marker" { 
      let marker = Marker() 
      self.markers.append(marker) 

      // push responsibility 
      parser.delegate = marker 

      // let marker know who we are 
      // so that once marker is done XML processing 
      // it can return parsing responsibility back 
      marker.parent = self 
     } 
    } 


} 

标记类如下:

class Marker : ParserBase { 

    var item = "" 
    var lat = 0 

    func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { 

     print("processing <\(elementName)> tag from Marker") 

     // if we finished an item tag, the ParserBase parent 
     // would have accumulated the found characters 
     // so just assign that to our item variable 
     if elementName == "item" { 
      self.item = foundCharacters 
     } 

      // similarly for lat tags 
      // convert the lat to an int for example 
     else if elementName == "lat" { 
      if let l = Int(foundCharacters) { 
       self.lat = l 
      } 
     } 

     // if we reached the </marker> tag, we do not 
     // have anything further to do, so delegate 
     // parsing responsibility to parent 
     else if elementName == "marker" { 
      parser.delegate = self.parent 
     } 

     // reset found characters 
     foundCharacters = "" 
    } 

} 

现在上分析,提取信息,并打印的东西。

let xmlData = xml.dataUsingEncoding(NSUTF8StringEncoding)! 
let parser = NSXMLParser(data: xmlData) 

let coord = Coord2() 
parser.delegate = coord 

parser.parse() 


print("coord has a count attribute of \(coord.count)") 
print("coord has \(coord.markers.count) markers") 

for marker in coord.markers { 
    print("marker item = \(marker.item) and lat = \(marker.lat)") 
} 

,其输出如下:

coord has a count attribute of 3 
coord has 2 markers 
marker item = marker1 and lat = 0 
marker item = marker2 and lat = 36 
+0

哇,非常感谢你的帮助@Benzi。还有一个问题。我在哪里放置XML文件的URL?我的机器上本地没有XML文件。另外,我把最后一块代码放在我的'override func viewDidLoad(){..}'函数中吗? – Ivan

+0

是的,你可以把最后一块代码放在'viewDidLoad'中......至于URL,你到目前为止如何使用解析器?如果你已经用类似parser = NSXMLParser(contentsOfURL:(NSURL(string:“http://xmfile.xml”))!)!'的方式构建它,那么就用它来代替最后一行的相应行块。 (我使用的是Playground,所以不得不将XML放在一个字符串中。) – Benzi

+0

太棒了。大多数情况下,除了将字符串转换为Double以外,其他所有工作都正常。我在你的回复中看到你将它投射到一个Int。我需要投到Double,但它不工作。我试着'var lat =“”','var lati = 0.0','self.lat = foundCharacters','self.lati =(self.lat as NSString).doubleValue'。 'self.lat'的值是“36.97728”,但'self.lati'将保持为0.0 – Ivan

4

我已经使用Class下创建以获得字典从XML数据。

https://github.com/Bhaavik/BDXmlParser

您需要添加类并调用下面函数字典响应

让objXmlParser = BbXmlParser() 让dictResponse = objXmlParser.getdictionaryFromXmlData(数据!) 打印(dictResponse) 在这里,你用字典去。 :)

+0

如果一个问题是另一个问题的重复,**标记**问题是重复的而不是回答。无论如何不要多次发布相同的答案。谢谢! – Moritz

+0

感谢您的建议!这是我的第一个答案,我正在学习东西:) – Bhavik

+0

然后,您应该阅读[参考]并查看[帮助]中的各种文章。它会帮助你做出更好的答案(我的意思是,遵循网站指南的答案)。 :) – Moritz

1

在雨燕2.2:

假设你的XML文件位于你的个人电脑上任何地方,让XML文件:

<!DOCTYPE suite SYSTEM "http://.dtd" > 

<packages> 
    <package name="packageone" /> 
</packages> 

<test name="testOne"> 
    <classes> 
     <class name="TestClassOne"> 
      <methods> 
       <include name="test_480" /> 
       <include name="test_481" /> 
       <include name="test_482" /> 
      </methods> 
     </class> 
    </classes> 
</test> 
<test name="testOne"> 
    <classes> 
     <class name="TestClassOne"> 
      <methods> 
       <include name="test_111" /> 
       <include name="test_112" /> 
       <include name="test_113" /> 
      </methods> 
     </class> 
    </classes> 
</test> 

所以,如果你wa NT从这个XML只读测试用例名称,使用下面的代码:

import Foundation 

class XmlParsing : NSObject, NSXMLParserDelegate { 

var element:String = "default" 
var testCaseName = [String]() 

func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { 

    if(elementName == "include"){ 

     //you can store the data in an array or any other way, if you want to use it in anywhere. 
     //testCaseName.append(attributeDict["name"]!) // this is used to store the test cases name 
     print(attributeDict["name"]!) 
    } 
    }  

} 

现在调用的功能等:

let path = NSURL(fileURLWithPath: "xml-file-location").URLByAppendingPathComponent("test.xml") 

var fileContent = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding) as String 

let data = fileContent.dataUsingEncoding(NSUTF8StringEncoding) //convert string to data for xml parsing 

let xmlParser = NSXMLParser(data: data!) //create xml parser 

let xmlRead = XmlParsing() // create the object of that class where function is declared. 

xmlParser.delegate = xmlRead //use that object to delegate 
xmlParser.parse() // parse the xml 

xmlParser.parse() 

被调用,比解析器功能被称为并执行必要的工作。输出是:

test_480 
test_481 
test_482 
test_111 
test_112 
test_113 

另一个重要的是解析器有重载函数。你可以使用它们作为你的要求。

例如,如果您需要从XML文件中读取任何

commented node 

,请使用XmlParsing类下面的解析器功能:

func parser(parser: NSXMLParser, foundComment comment: String) { 

    print(comment) 
} 

这是找到API链接查看详情:https://developer.apple.com/library/ios/documentation/Cocoa/Reference/NSXMLParserDelegate_Protocol/#//apple_ref/occ/intfm/NSXMLParserDelegate/parser:foundComment