Created
July 17, 2024 02:40
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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