Swift中如何用XMLParser解析XML元素的属性值?
解决Swift中XMLParserDelegate提取元素属性值的问题
问题描述
需要解析METAR格式的XML数据,目前已通过XMLParserDelegate成功获取元素标签内的文本内容(例如<flight_category>IFR</flight_category>中的IFR),但无法提取元素属性的值,比如从<sky_condition sky_cover="OVC" cloud_base_ft_agl="2100"/>中获取sky_cover对应的OVC值。
待解析XML示例
<METAR> <sky_condition sky_cover="OVC" cloud_base_ft_agl="2100"/> <!-- 需提取sky_cover的OVC --> <flight_category>IFR</flight_category> <metar_type>METAR</metar_type> </METAR>
现有代码
FeedParser类
import Foundation class FeedParser: NSObject, XMLParserDelegate { private var airportItems: [FlightCategory] = [] // 跟踪解析进度 private var currentAirportElement: String = "" // 跟踪当前解析的元素 private var parserCompletiongHandler: (([FlightCategory]) -> Void)? // 解析完成回调 private var currentAirportFlightCategory: String = "" { didSet { // 去除空白字符 currentAirportFlightCategory = currentAirportFlightCategory.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } } private var currenAirportSkyCondition: String = "" { didSet { currenAirportSkyCondition = currenAirportSkyCondition.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } } // 解析XML Feed func parseFeed(url: String, completionHandler: (([FlightCategory]) -> Void)?) { self.parserCompletiongHandler = completionHandler let request = URLRequest(url: URL(string: url)!) let session = URLSession.shared let task = session.dataTask(with: request) { data, response, error in guard let data = data else { if let error = error { print(error.localizedDescription) } return } let parser = XMLParser(data: data) parser.delegate = self parser.parse() } task.resume() } // MARK: - XML Parser Delegate func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { currentAirportElement = elementName if currentAirportElement == "METAR" { currentAirportFlightCategory = "" currenAirportSkyCondition = "" } } func parser(_ parser: XMLParser, foundCharacters string: String) { switch currentAirportElement { case "flight_category": currentAirportFlightCategory += string default: break } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == "METAR" { let airportItem = FlightCategory(flight_category: currentAirportFlightCategory, sky_condition: currenAirportSkyCondition) self.airportItems.append(airportItem) } } func parserDidEndDocument(_ parser: XMLParser) { parserCompletiongHandler?(airportItems) } func parser(_ parser: XMLParser, parseErrorOccurred parseError: any Error) { print(parseError.localizedDescription) } }
XML加载代码
private func loadXMLData() async { print("hmmmmm") let acwXMLURL = "https://aviationweather.gov/api/data/dataserver?requestType=retrieve&dataSource=metars&stationString=KVNY&startTime=2024-03-10T21%3A44%3A22Z&format=xml&mostRecent=true" let feeder = FeedParser() feeder.parseFeed(url: acwXMLURL) { data in self.detaileHomeAirportXML = data } } private func handleXMLData() async { detaileHomeAirportXML?.forEach { item in currentFltCat = item.flight_category model.airportFltCat = item.flight_category print("XML data handled: \(currentFltCat)") if currentFltCat == "VFR" { color = Color(.green) } else if currentFltCat == "MVFR" { color = Color(.blue) } else if currentFltCat == "IFR" { color = Color(.red) } else if currentFltCat == "LIFR" { color = Color(red: 208 / 255, green: 45 / 255, blue: 208 / 255) } } }
解决方法
核心是利用XMLParserDelegate的parser(_:didStartElement:namespaceURI:qualifiedName:attributes:)方法中的attributeDict参数,该参数包含了当前元素的所有属性键值对。对于自闭合的<sky_condition>标签,直接在这个方法中提取属性值即可,无需通过foundCharacters处理。
修改后的关键代码部分
修改parser(_:didStartElement:namespaceURI:qualifiedName:attributes:)方法:
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { currentAirportElement = elementName if currentAirportElement == "METAR" { currentAirportFlightCategory = "" currenAirportSkyCondition = "" } else if currentAirportElement == "sky_condition" { // 从属性字典中提取sky_cover的值,为空则设为空字符串 currenAirportSkyCondition = attributeDict["sky_cover"] ?? "" } }
修改后的完整FeedParser类
import Foundation class FeedParser: NSObject, XMLParserDelegate { private var airportItems: [FlightCategory] = [] // 跟踪解析进度 private var currentAirportElement: String = "" // 跟踪当前解析的元素 private var parserCompletiongHandler: (([FlightCategory]) -> Void)? // 解析完成回调 private var currentAirportFlightCategory: String = "" { didSet { // 去除空白字符 currentAirportFlightCategory = currentAirportFlightCategory.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } } private var currenAirportSkyCondition: String = "" { didSet { currenAirportSkyCondition = currenAirportSkyCondition.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) } } // 解析XML Feed func parseFeed(url: String, completionHandler: (([FlightCategory]) -> Void)?) { self.parserCompletiongHandler = completionHandler let request = URLRequest(url: URL(string: url)!) let session = URLSession.shared let task = session.dataTask(with: request) { data, response, error in guard let data = data else { if let error = error { print(error.localizedDescription) } return } let parser = XMLParser(data: data) parser.delegate = self parser.parse() } task.resume() } // MARK: - XML Parser Delegate func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { currentAirportElement = elementName if currentAirportElement == "METAR" { currentAirportFlightCategory = "" currenAirportSkyCondition = "" } else if currentAirportElement == "sky_condition" { // 提取sky_cover属性值,为空则设为空字符串 currenAirportSkyCondition = attributeDict["sky_cover"] ?? "" } } func parser(_ parser: XMLParser, foundCharacters string: String) { switch currentAirportElement { case "flight_category": currentAirportFlightCategory += string default: break } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if elementName == "METAR" { let airportItem = FlightCategory(flight_category: currentAirportFlightCategory, sky_condition: currenAirportSkyCondition) self.airportItems.append(airportItem) } } func parserDidEndDocument(_ parser: XMLParser) { parserCompletiongHandler?(airportItems) } func parser(_ parser: XMLParser, parseErrorOccurred parseError: any Error) { print(parseError.localizedDescription) } }
说明
<sky_condition>是自闭合标签,没有内部文本内容,所以不需要在foundCharacters方法中处理它的内容。- 通过
attributeDict["sky_cover"]直接获取属性值,使用?? ""处理属性不存在的情况,避免空值。
内容的提问来源于stack exchange,提问作者Yuda
相关产品推荐
相关产品推荐

