Синтаксический анализ XML из URL в Swift
Я совершенно новичок в синтаксическом анализе и не могу найти ни одного учебника, который не устарел и не вызывает больше вопросов. У меня есть простой url-адрес xml-файла, который я пытаюсь разобрать. Xml очень прост:
<xml>
<record>
<EmpName>A Employee</EmpName>
<EmpPhone>111-222-3333</EmpPhone>
<EmpEmail>[email protected]</EmpEmail>
<EmpAddress>12345 Fake Street</EmpAddress>
<EmpAddress1>MyTown, Mystate ZIP</EmpAddress1>
</record>
</xml>
И просто хотел сохранить это как NSDictionary (теги как ключи и данные как значения). До сих пор все, что мне удалось сделать успешно, это распечатать строку xml в консоли с помощью:
let url = NSURL(string: "http://www.urlexample.com/file.xml")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
print(task)
task.resume()
Я просмотрел все онлайн-учебники, которые я нашел, и они либо устарели, либо слишком устарели. сложный. Любая помощь ценится.
3 ответов:
Процесс прост:
- создайте
XMLParserобъект, передав ему данные.- укажите
delegateдля этого синтаксического анализатора.- провести анализ.
Итак, в Swift 3/4 это выглядит следующим образом:
Вопрос в том, как вы реализуете методыlet task = URLSession.shared.dataTask(with: url) { data, response, error in guard let data = data, error == nil else { print(error ?? "Unknown error") return } let parser = XMLParser(data: data) parser.delegate = self if parser.parse() { print(self.results ?? "No results") } } task.resume()XMLParserDelegate. Три критических метода:didStartElement(где вы готовитесь к получению символов),foundCharacters(где вы обрабатываете фактические значения, проанализированные) иdidEndElement(где вы сохраняете результаты).Вы спросили, как для разбора одной записи (то есть одного словаря), но я покажу вам более общий шаблон для разбора ряда из них, что является гораздо более распространенной ситуацией с XML. Очевидно, вы можете увидеть, как упростить это, если вам не нужен массив значений (или просто захватить первый).
// a few constants that identify what element names we're looking for inside the XML // a few constants that identify what element names we're looking for inside the XML let recordKey = "record" let dictionaryKeys = Set<String>(["EmpName", "EmpPhone", "EmpEmail", "EmpAddress", "EmpAddress1"]) // a few variables to hold the results as we parse the XML var results: [[String: String]]? // the whole array of dictionaries var currentDictionary: [String: String]? // the current dictionary var currentValue: String? // the current value for one of the keys in the dictionaryИ
extension ViewController: XMLParserDelegate { // initialize results structure func parserDidStartDocument(_ parser: XMLParser) { results = [] } // start element // // - If we're starting a "record" create the dictionary that will hold the results // - If we're starting one of our dictionary keys, initialize `currentValue` (otherwise leave `nil`) func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { if elementName == recordKey { currentDictionary = [:] } else if dictionaryKeys.contains(elementName) { currentValue = "" } } // found characters // // - If this is an element we care about, append those characters. // - If `currentValue` still `nil`, then do nothing. func parser(_ parser: XMLParser, foundCharacters string: String) { currentValue? += string } // end element // // - If we're at the end of the whole dictionary, then save that dictionary in our array // - If we're at the end of an element that belongs in the dictionary, then save that value in the dictionary func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == recordKey { results!.append(currentDictionary!) currentDictionary = nil } else if dictionaryKeys.contains(elementName) { currentDictionary![elementName] = currentValue currentValue = nil } } // Just in case, if there's an error, report it. (We don't want to fly blind here.) func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { print(parseError) currentValue = nil currentDictionary = nil results = nil } }Для перевода Swift 2 смотритепредыдущую редакцию этого ответа .
Ответ Роба для Свифта 3 / 4
func getDataFrom(url: URL, completion: @escaping (_ data: Data?, _ error: Error?)->()) { let session = URLSession(configuration: .default) let download = session.dataTask(with: url) { data, response, error in completion(data, error) } download.resume() } getDataFrom(url: url) { data, error in guard let data = data else { return } let parser = XMLParser(data: data) parser.delegate = self if parser.parse() { print(self.results) } } // a few constants that identify what element names we're looking for inside the XML let recordKey = "record" let dictionaryKeys = ["EmpName", "EmpPhone", "EmpEmail", "EmpAddress", "EmpAddress1"] // a few variables to hold the results as we parse the XML var results: [[String: String]]! // the whole array of dictionaries var currentDictionary: [String: String]! // the current dictionary var currentValue: String? // start element // // - If we're starting a "record" create the dictionary that will hold the results // - If we're starting one of our dictionary keys, initialize `currentValue` (otherwise leave `nil`) func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { if elementName == recordKey { self.currentDictionary = [String : String]() } else if dictionaryKeys.contains(elementName) { self.currentValue = String() } } // found characters // // - If this is an element we care about, append those characters. // - If `currentValue` still `nil`, then do nothing. func parser(_ parser: XMLParser, foundCharacters string: String) { self.currentValue? += string } // end element // // - If we're at the end of the whole dictionary, then save that dictionary in our array // - If we're at the end of an element that belongs in the dictionary, then save that value in the dictionary func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == self.recordKey { self.results.append(self.currentDictionary) self.currentDictionary = nil } else if dictionaryKeys.contains(elementName) { self.currentDictionary[elementName] = currentValue self.currentValue = nil } } // Just in case, if there's an error, report it. (We don't want to fly blind here.) func parser(_ parser: XMLParser, parseErrorOccurred parseError: Error) { print(parseError) self.currentValue = nil self.currentDictionary = nil self.results = nil }
Я написал модуль для отображения XML в объекты, называемый XMLMapper. (использует ту же технику, что и ObjectMapper )
Для того, чего вы хотите достичь, вы можете просто использовать класс
XMLSerialization, например:let url = URL(string: "http://www.urlexample.com/file.xml") let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in do{ let xmlDictionary = try XMLSerialization.xmlObject(with: data!) as? [String: Any] } catch { print("Serialization error occurred: \(error.localizedDescription)") } } task.resume()Вы также можете реализовать протокол
XMLMappableследующим образом:class XMLResponse: XMLMappable { var nodeName: String! var record: Record? required init(map: XMLMap) { } func mapping(map: XMLMap) { record <- map["record"] } } class Record: XMLMappable { var nodeName: String! var empName: String! var empPhone: String! var empEmail: String? var empAddress: String? var empAddress1: String? required init(map: XMLMap) { } func mapping(map: XMLMap) { empName <- map["EmpName"] empPhone <- map["EmpPhone"] empEmail <- map["EmpEmail"] empAddress <- map["EmpAddress"] empAddress1 <- map["EmpAddress1"] } }И сопоставить XML-ответ в эти объекты с помощью класса
XMLMapper:let xmlResponse = XMLMapper<XMLResponse>().map(XMLObject: xmlDictionary)Попробуйте.
Comments