What is SWXMLHash and how does it work?
mainXMLParser (formerly NSXMLParser). It provides a conceptual translation from XML structures into a dictionary of arrays (a hash), making XML data easier to navigate in a way similar to SwiftyJSON.repository·main·Indexed 23 days ago
https://github.com/drmohundro/swxmlhashA Swift library that provides a high-level wrapper around XMLParser to translate XML into a dictionary-like structure (hash). It supports subscript syntax for element lookups, attribute access, lazy processing for large files, and custom object deserialization via the XMLObjectDeserialization and XMLValueDeserialization protocols.
XMLParser (formerly NSXMLParser). It provides a conceptual translation from XML structures into a dictionary of arrays (a hash), making XML data easier to navigate in a way similar to SwiftyJSON.Errors can be handled using standard Swift do-catch blocks with IndexingError, or by switching on the result of an indexer lookup.
Note: Error handling via do-catch or switching on the result will not work with lazy loaded XML, because parsing only occurs when .element or .all is called, making it impossible to know if an element exists prior to that call.
To install using CocoaPods, add pod 'SWXMLHash', '~> 7.0.0' to your Podfile within your target block. Ensure use_frameworks! is enabled. Run pod install to complete the installation.
platform :ios, '10.0'
use_frameworks!
target 'YOUR_TARGET_NAME' do
pod 'SWXMLHash', '~> 7.0.0'
endTo add SWXMLHash as a dependency in your Swift project, update the dependencies array in your Package.swift file. Use a version reference starting from 7.0.0 to ensure compatibility.
dependencies: [
.package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "7.0.0")
]Add the following line to your Cartfile to include SWXMLHash via Carthage:
github "drmohundro/SWXMLHash" ~> 7.0
github "drmohundro/SWXMLHash" ~> 7.0.swift files into your project or include the SWXMLHash.xcodeproj if you are using a workspace.To map XML to custom Swift types, implement the XMLObjectDeserialization protocol. This allows you to use .value() to convert an XML node directly into an array or a single instance of your custom type.
Built-in converters support Int, Double, Float, Bool, and String (including optional variants) for leaf nodes and attributes.
struct Book: XMLObjectDeserialization {
let title: String
let price: Double
let year: Int
let amount: Int?
let isbn: Int
let category: [String]
static func deserialize(_ node: XMLIndexer) throws -> Book {
return try Book(
title: node["title"].value(),
price: node["price"].value(),
year: node["year"].value(),
amount: node["amount"].value(),
isbn: node.value(ofAttribute: "isbn"),
category : node["categories"]["category"].value()
)
}
}
// Usage
let books: [Book] = try xml["root"]["books"]["book"].value()This error occurs during deserialization when the type on the left-hand side of the expression does not implement XMLObjectDeserialization (for groups) or XMLElementDeserializable (for single elements).
For example, calling .value() to assign to a Date will fail because Date does not have a built-in deserializer in SWXMLHash. To fix this, you must implement the XMLElementDeserializable protocol for your custom type.
Attributes can be accessed in two ways:
.element?.attribute(by: "name") on an element..withAttribute("name", "value") method to filter for elements that possess a specific attribute value.You can filter elements using .filterAll (to filter based on the element itself) and .filterChildren (to filter based on the index of the child).
let subIndexer = xml!["root"]["catalog"]["book"]
.filterAll { elem, _ in elem.attribute(by: "id")!.text == "bk102" }
.filterChildren { _, index in index >= 1 && index <= 3 }
print(subIndexer.children[0].element?.text)If you need to convert a string value from XML into a custom type (like Date), implement the XMLValueDeserialization protocol. You must provide implementations for deserialize(_ element: XMLHash.XMLElement) and deserialize(_ attribute: XMLAttribute).
extension Date: XMLValueDeserialization {
public static func deserialize(_ element: XMLHash.XMLElement) throws -> Date {
let date = stringToDate(element.text)
guard let validDate = date else {
throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)
}
return validDate
}
public static func deserialize(_ attribute: XMLAttribute) throws -> Date {
let date = stringToDate(attribute.text)
guard let validDate = date else {
throw XMLDeserializationError.attributeDeserializationFailed(type: "Date", attribute: attribute)
}
return validDate
}
public func validate() throws {}
private static func stringToDate(_ dateAsString: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE, dd MMMM yyyy HH:mm:ss SSS"
return dateFormatter.date(from: dateAsString)
}
}
// Usage
let dt: Date = try xml["root"]["elem"].value()Use .all to iterate over all nodes at the current indexed level. Use .children to iterate over the immediate child elements of an indexer.
// Iterate over all elements at current level
for elem in xml["root"]["catalog"]["book"].all {
print(elem["genre"].element!.text!)
}
// Recursive enumeration of all children
func enumerate(indexer: XMLIndexer) {
for child in indexer.children {
print(child.element!.name)
enumerate(child)
}
}