Skip to content

Instantly share code, notes, and snippets.

@yodaluca23
Created July 17, 2024 02:40
Show Gist options
  • Save yodaluca23/e39b3ff29e3eb21e5802fd9319797cbf to your computer and use it in GitHub Desktop.
Save yodaluca23/e39b3ff29e3eb21e5802fd9319797cbf to your computer and use it in GitHub Desktop.
Convert XML data to a swift dictionary using Foundation's XML Parser handles nestled elements, converts to array if same named keys.
import Foundation
import FoundationXML
class XMLDictionaryParser: NSObject, XMLParserDelegate {
private var dictionaryStack: [[String: Any]] = []
private var textInProgress: String = ""
func parse(data: Data) -> [String: Any]? {
let parser = XMLParser(data: data)
parser.delegate = self
guard parser.parse() else {
return nil
}
return dictionaryStack.first
}
// MARK: - XMLParserDelegate
func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
var dict: [String: Any] = [:]
for (key, value) in attributeDict {
if let intValue = Int(value) {
dict[key] = intValue
} else {
dict[key] = value
}
}
dictionaryStack.append(dict)
textInProgress = ""
}
func parser(_ parser: XMLParser, foundCharacters string: String) {
textInProgress += string
}
func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
var dict = dictionaryStack.popLast()!
if !textInProgress.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
if let intValue = Int(textInProgress.trimmingCharacters(in: .whitespacesAndNewlines)) {
dict[elementName] = intValue
} else {
dict[elementName] = textInProgress.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
if var top = dictionaryStack.last {
if let existingValue = top[elementName] {
if var array = existingValue as? [[String: Any]] {
array.append(dict)
top[elementName] = array
} else {
top[elementName] = [existingValue, dict]
}
} else if dict.count == 1, let key = dict.keys.first, let value = dict[key] {
top[elementName] = value
} else {
top[elementName] = dict
}
dictionaryStack[dictionaryStack.count - 1] = top
} else {
dictionaryStack.append(dict)
}
textInProgress = ""
}
}
// Example Usage
let XMLString = """
<root>
<person>
<name>John Doe</name>
<age>30</age>
<address>New York, USA</address>
</person>
<person>
<name>Jane Smith</name>
<age>25</age>
<address>London, UK</address>
</person>
</root>
"""
if let XMLdata = XMLString.data(using: .utf8) {
let parser = XMLDictionaryParser()
if let parsedDictionary = parser.parse(data: XMLdata) {
if let root = parsedDictionary["root"] as? [String: Any],
let persons = root["person"] as? [[String: Any]] {
for person in persons {
if let name = person["name"] as? String {
print(name)
}
}
}
} else {
print("Parsing failed")
}
} else {
print("Invalid XML string")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment