Вот образец данных XML, которые я пытаюсь проанализировать. У меня возникли проблемы с выяснением того, как анализировать данные внутри "", вложенного в скобки, т. е. <something="Как получить эту строку">. вот, например, идентификатор хочу получить «Как получить эту строку». Я могу получить данные, если они находятся между открывающейся и закрывающейся морковкой. Я понял.
<METAR>
<sky_condition sky_cover = "OVC" cloud_base_ft_agl = "2100"/> <---- get the OVC from sky_cover
<flight_category>IFR</flight_category>
<metar_type>METAR</metar_type>
</METAR>
Вот мой код XML-парсера и функции делегирования
import Foundation
class FeedParser: NSObject, XMLParserDelegate {
private var airportItems: [FlightCategory] = [] // track parsing
private var currentAirportElement: String = "" // track current item being parsed
private var parserCompletiongHandler: (([FlightCategory]) -> Void)? // handle completion of parsing
private var currentAirportFlightCategory: String = "" {
didSet {
// trim whitespace
currentAirportFlightCategory = currentAirportFlightCategory.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}
private var currenAirportSkyCondition: String = "" {
didSet {
currenAirportSkyCondition = currenAirportSkyCondition.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}
// MARK: - URL Request
// Hnadle Parsing feed
func parseFeed(url: String, completionHandler: (([FlightCategory]) -> Void)?) {
self.parserCompletiongHandler = completionHandler
// URL Session Reques
let request = URLRequest(url: URL(string: url)!)
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
// if no data
guard let data = data else {
if let error = error {
print(error.localizedDescription)
}
return
}
// if data
// parse xml data
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] = [:]) {
// track current item
currentAirportElement = elementName
if currentAirportElement == "METAR" { // it is there
// currentAirportElement = ""
currentAirportFlightCategory = ""
currenAirportSkyCondition = ""
}
}
// Once Parser get values of element Handler
func parser(_ parser: XMLParser, foundCharacters string: String) {
switch currentAirportElement {
case "flight_category": currentAirportFlightCategory += string
default: break
}
}
// Once Parser at End of element Handler
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
if elementName == "METAR" { // "flight_category"
let airportItems = FlightCategory(flight_category: currentAirportFlightCategory, sky_condition: currenAirportSkyCondition)
self.airportItems.append(airportItems)
}
}
// Call Completion Handler upon finishing Parsing
func parserDidEndDocument(_ parser: XMLParser) {
parserCompletiongHandler?(airportItems)
}
// Handle Parsing Errors
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"
// parser feeder
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)
}
}
}
Раш, я об этом думал, но я могу представить, как это будет выглядеть, и я в замешательстве, что будет с кодом.
Посмотрите ссылку, которую я только что разместил перед вашим ответом.
почему бы не использовать более современный подход с использованием конечной точки API данных JSON. https://aviationweather.gov/api/data/metar?stationString=\KVNY&startTime=2024-03-10T21%3A44%3A22Z&format=xml&mostRecent=true&format=json
Его гораздо проще декодировать и обрабатывать, особенно при использовании более современного асинхронного кода let (data, response) = try await URLSession.shared.data(from: url)
. Вам не придется писать сотни строк кода для работы со старым XML.
к сожалению, aviatationweather использовала XML для большого кеша, что является конечным результатом, поскольку я могу искать доступные аэропорты. Я использовал json, но это стало камнем преткновения, потому что есть одно значение, которое я не могу найти через JSON, а именно категория полета аэропорта. Кажется, это только в XML
«Вещь», которую вы ищете, называется атрибутом (а «морковки» называются начальным и конечным тегами). Как только вы выучите базовый словарный запас, поиск информации в Google станет очень простым. Если вы изобретаете свой собственный словарь, это невозможно.
Скопируй это! Я забыл многое из этого, когда прекратил работу над CS — только приступив к этому, я вспомнил, насколько привлекательным и понятным был процесс Stakeover. Однако я очень ценю, что вы напомнили мне, как они называются, это определенно поможет!
В функции делегирования DidStartElement вы получаете словарь attributes
. Он содержит атрибуты тега.
Вы бы написали что-то вроде -
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
switch(elementName) {
case "METAR":
currentAirportFlightCategory = ""
currenAirportSkyCondition = ""
case "sky_condition":
skyCover = attributes["sky_cover"]
cloudBase = attributes["cloud_base_ft_agl"]
default:
print("Ignoring element \(elementName)")
}
}
Обратите внимание, что это будут необязательные строки, поэтому вам нужно обработать их и при необходимости преобразовать в целое число.
К вашему сведению: по этому вопросу много дубликатов.
Наверное, но было проще написать четкий ответ, который поможет ОП, чем продираться в поисках хорошего обмана.
Я пробовал - их было так много, и я знаю, что не помогу, но не смог найти ответ. Я вижу, что вы сделали, но я не понимаю, как я могу обновить FoundCharacters, чтобы затем назначить значение currenAirportSkyCondition is = sky_cover.
Получил работу!! Спасибо — отмечу как ответ, просто запутался — кажется, мне все равно нужно использовать функцию FoundCharacters для присвоения значений, не заключенных в <>, это правильно?
Символы вне элементов представляют собой текстовые блоки, поэтому да, вам нужно использовать didStartElement
, чтобы идентифицировать текущий элемент, а затем вы можете использовать foundCharacters
, чтобы получить текст, связанный с этим элементом. Вероятно, есть веская причина, по которой JSON в наши дни гораздо более популярен, чем XML. Как отметил WorkDog, у этого сервиса есть конечная точка JSON. Благодаря Swift с ним будет намного проще работать Codable
Я согласен, что так и было бы, однако Aviationweather использует xml для больших файлов кэша, и есть определенные атрибуты, к которым его JSON-файл предоставляет доступ, в основном и категория полета, что сэкономило мне сотни строк кода, потому что в противном случае мне пришлось бы лично определять каждый рейс. категория
В методе делегата
didStartElement
посмотрите на параметрattributes
. В этом словаре есть нужные вам данные. Ключи — это имена атрибутов, а значения — значения атрибутов.