Swift minion for simple and lightweight XML parsing

Related tags

XML & HTML AEXML
Overview

Swift 5.3 Platforms iOS | watchOS | tvOS | macOS CocoaPods Carthage Swift Package Manager License MIT

AEXML

Swift minion for simple and lightweight XML parsing

I made this for personal use, but feel free to use it or contribute. For more examples check out Sources and Tests.

Index

Intro

This is not a robust full featured XML parser, but rather simple, lightweight and easy to use utility for casual XML handling.

Features

  • Read XML data
  • Write XML string
  • Covered with unit tests
  • Covered with inline docs

Usage

Read XML

Let's say this is some XML string you picked up somewhere and made a variable data: Data from that.

<?xml version="1.0" encoding="utf-8"?>
<animals>
    <cats>
        <cat breed="Siberian" color="lightgray">Tinna</cat>
        <cat breed="Domestic" color="darkgray">Rose</cat>
        <cat breed="Domestic" color="yellow">Caesar</cat>
        <cat></cat>
    </cats>
    <dogs>
        <dog breed="Bull Terrier" color="white">Villy</dog>
        <dog breed="Bull Terrier" color="white">Spot</dog>
        <dog breed="Golden Retriever" color="yellow">Betty</dog>
        <dog breed="Miniature Schnauzer" color="black">Kika</dog>
    </dogs>
</animals>

This is how you can use AEXML for working with this data:
(for even more examples, look at the unit tests code included in project)

guard let
    let xmlPath = Bundle.main.path(forResource: "example", ofType: "xml"),
    let data = try? Data(contentsOf: URL(fileURLWithPath: xmlPath))
else { return }

do {
    let xmlDoc = try AEXMLDocument(xml: data, options: options)
        
    // prints the same XML structure as original
    print(xmlDoc.xml)
    
    // prints cats, dogs
    for child in xmlDoc.root.children {
        print(child.name)
    }
    
    // prints Optional("Tinna") (first element)
    print(xmlDoc.root["cats"]["cat"].value)
    
    // prints Tinna (first element)
    print(xmlDoc.root["cats"]["cat"].string)
    
    // prints Optional("Kika") (last element)
    print(xmlDoc.root["dogs"]["dog"].last?.value)
    
    // prints Betty (3rd element)
    print(xmlDoc.root["dogs"].children[2].string)
    
    // prints Tinna, Rose, Caesar
    if let cats = xmlDoc.root["cats"]["cat"].all {
        for cat in cats {
            if let name = cat.value {
                print(name)
            }
        }
    }
    
    // prints Villy, Spot
    for dog in xmlDoc.root["dogs"]["dog"].all! {
        if let color = dog.attributes["color"] {
            if color == "white" {
                print(dog.string)
            }
        }
    }
    
    // prints Tinna
    if let cats = xmlDoc.root["cats"]["cat"].all(withValue: "Tinna") {
        for cat in cats {
            print(cat.string)
        }
    }
    
    // prints Caesar
    if let cats = xmlDoc.root["cats"]["cat"].all(withAttributes: ["breed" : "Domestic", "color" : "yellow"]) {
        for cat in cats {
            print(cat.string)
        }
    }
    
    // prints 4
    print(xmlDoc.root["cats"]["cat"].count)
    
    // prints Siberian
    print(xmlDoc.root["cats"]["cat"].attributes["breed"]!)
    
    // prints <cat breed="Siberian" color="lightgray">Tinna</cat>
    print(xmlDoc.root["cats"]["cat"].xmlCompact)
    
    // prints Optional(AEXML.AEXMLError.elementNotFound)
    print(xmlDoc["NotExistingElement"].error)
}
catch {
    print("\(error)")
}

Write XML

Let's say this is some SOAP XML request you need to generate.
Well, you could just build ordinary string for that?

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soap:Header>
    <m:Trans xmlns:m="http://www.w3schools.com/transaction/" soap:mustUnderstand="1">234</m:Trans>
  </soap:Header>
  <soap:Body>
    <m:GetStockPrice>
      <m:StockName>AAPL</m:StockName>
    </m:GetStockPrice>
  </soap:Body>
</soap:Envelope>

Yes, but, you can also do it in a more structured and elegant way with AEXML:

// create XML Document
let soapRequest = AEXMLDocument()
let attributes = ["xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance", "xmlns:xsd" : "http://www.w3.org/2001/XMLSchema"]
let envelope = soapRequest.addChild(name: "soap:Envelope", attributes: attributes)
let header = envelope.addChild(name: "soap:Header")
let body = envelope.addChild(name: "soap:Body")
header.addChild(name: "m:Trans", value: "234", attributes: ["xmlns:m" : "http://www.w3schools.com/transaction/", "soap:mustUnderstand" : "1"])
let getStockPrice = body.addChild(name: "m:GetStockPrice")
getStockPrice.addChild(name: "m:StockName", value: "AAPL")

// prints the same XML structure as original
print(soapRequest.xml)

Installation

License

AEXML is released under the MIT license. See LICENSE for details.

Comments
  • Swift 3 Support

    Swift 3 Support

    Hi,

    I installed the latest Xcode with Swift 3.0, and AEXML generates numerous errors. Would appreciate a new release which supports.

    Thanks much, Brian.

    opened by appsird 27
  • receive Bound value in a conditional binding must be of Optional type

    receive Bound value in a conditional binding must be of Optional type

    line 223 of AEXML.swift has:

              if let parser = NSXMLParser(data: xmlData) {
    

    Though, it appears as of the most recent Xcode, the following is required:

    if let parser = NSXMLParser(data: xmlData) as NSXMLParser! {

    Can you please validate my change is correct, and why you are not seeing this issue.

    Thanks, Brian

    opened by appsird 15
  • Escaping special characters

    Escaping special characters

    It seems that AEXML's xmlstring property does not escape characters like "greater than", "less than" and "ampersand". This in turn leads to invalid XML being returned if an element's value contains such a character. Alternatively escaping could also be done when adding an element. Nor sure which is the better way...

    opened by ghowen 14
  • Why aren't double, int, bool and string properties of AEXMLElement optionals?

    Why aren't double, int, bool and string properties of AEXMLElement optionals?

    Let's say I've created an AEXMLElement object from API data and I assume its value is a double but I'm obviously not sure, since it's coming from an external source.

    If I then use the AEXMLElement object's double property, I have no way of knowing if the data was invalid, I will just get 0.0 if it's invalid, but that may be valid data. Why isn't double an optional, so I can check if it's actually valid? As far as I can see I would have to check if value is non-nil and can be cast to Double myself.

    opened by mattsson 11
  • Memory consumption

    Memory consumption

    Actually, your parser consume a lot of memory in Xcode 9 beta (both 1 and 2) with Swift 4. The problem is with appending string during parsing XML in Parser.swift. Appending founded string into currentValue allocate new memory, but still hold old one. I'm not sure if it's problem of implementation of calling delegate from XMLParser or they changed something and transfer responsibility of management into implementation of the delegate. Solution is fold processing of func parser(_ parser: XMLParser, foundCharacters string: String) into autoreleasepool closure.

    Does have anybody else that problem with Swift 4?

    bug help wanted 
    opened by milanhorvatovic 10
  • Parsing Element within Another element loses the value of the first Element

    Parsing Element within Another element loses the value of the first Element

    I'm parsing some Shakespeare xml files with AEXML (which is working great for me otherwise BTW) when I experience some problems getting the correct values out of elements from the following xml:

    SAMPSON Aside to GREGORY Is the law of our side, if I say ay?

    I'm running through the SPEECH element (speechElement:AEXMLElement)

    for child in speechElement.children { // when child equals the the first LINE element child.value // should equal "Is the law of our side, if I say" but is nil }

    The LINE element contains the STAGEDIR element correctly, but loses anything after the STAGEDIR.

    I've also moved the STAGEDIR element in the line, like so: Is the law of Aside to GREGORY our side, if I say and " our side, if I say" is lost.

    I've worked around it by modifying the xml files, but I wanted to file a bug and let you know.

    opened by willchen 9
  • "Cast from AEXMLElement to unrelated type ___ always fails"

    I'm not sure if this is somehow a problem with AEXML or a bug in XCode, or some other weird thing, so pardon me if it has nothing to do with this library.

    Somehow, I'm getting an error on a file that doesn't use AEXML that there is a cast from AEXMLElement. If I comment out the #import AEXML line (in a totally unrelated file) then the project builds. Then when I uncomment the import, it builds fine.

    I put together a simple sample project to illustrate this.

    https://github.com/aaronpk/AEXML_Test

    opened by aaronpk 8
  • Subscript Returns Error String as Value of AEXMLElement is Surprising (To Me)

    Subscript Returns Error String as Value of AEXMLElement is Surprising (To Me)

    It cramps my style. :smile_cat: It also feels awkward having to type:

    let element = elementList["MyElement"]
    if element.name != AEXMLElement.errorElementName {
        let string = element["MyString"].stringValue
    }
    

    as opposed to something like:

    let element = elementList["MyElement"]
    let string = element["MyString"].stringValue // I would expect string == ""
    

    because 99% of the time I don't want the string, "element <MyString> not found", because I use stringValue. Was under the assumption that stringValue would return an empty string upon lookup error, not an error string.

    This is coming from having used SwiftyJSON. So that is where my expectations are coming from. Feel free to disagree if that's how you feel.

    Thoughts?

    opened by bhendersonizeni 7
  • Build error: `error: instance member 'document' cannot be used on type`

    Build error: `error: instance member 'document' cannot be used on type`

    Sources/Parser.swift:18:20: error: instance member 'document' cannot be used on type 'AEXMLParser'
            let trim = document.options.parserSettings.shouldTrimWhitespace
                       ^~~~~~~~
    

    It appears this regression was added recently in this commit: https://github.com/tadija/AEXML/commit/506c15df3160f720b58b543fa7b42226e21e1463

    opened by rahul-malik 6
  • Updated project settings and podspec targets to use SWIFT_VERSION 3

    Updated project settings and podspec targets to use SWIFT_VERSION 3

    This fixes the problem where Xcode pops a warning that it needs to convert the project source into Swift 3, since it isn't sure that the code's been converted already.

    opened by vicng 6
  • Swift package

    Swift package

    I added the Package.swift configuration file in order to work with the new Swift Package Manager.

    I only had to do one change in the code and that was to remove the var keyword in the indentation function declaration. All tests kept on running, so this shouldn't be a problem.

    opened by fousa 6
  • Error while parsing large files on linux

    Error while parsing large files on linux

    Hello, I got an issue when parsing large files on linux. I got the following issue : Error Domain=NSXMLParserErrorDomain Code=1 "(null)"

    When I compile and run on MacOS everything runs fine

    opened by JeanRomain 0
  • Question

    Question

    Hello! I have a question When I parse an e-book, I get a mass of XML files Then I do as in your example and combine everything in one line

    let document = try AEXMLDocument (xml: data) var parsedText = String () // parse unknown structure for child in document.root.children { parsedText + = child.xml + "\ n" } After that I get one long line of code, realizing that this is XML, how can I convert it to HTML to display it in the WebView, or how to convert it to tex to display it in TextView?

    help wanted question 
    opened by rastaman111 0
  • Support for DTDs?

    Support for DTDs?

    I would like to specify the correct DTD for the XML file used. How should I do this? Right now I consider enhancing AEXMLOptions.DocumentHeader with 3 additional optional values which would allow specifying the correct settings for the DTD (see eg. https://xmlwriter.net/xml_guide/doctype_declaration.shtml). Any better idea?

    Many thanks in advance!

    enhancement help wanted 
    opened by Vithanco 4
  • Not reading all nodes

    Not reading all nodes

    Didn't see this issue in the documentation but it's only hitting the first node in my XML document. For example I have multiple "Alert" nodes

    <CTAAlerts>
    <TimeStamp>20171127 11:47</TimeStamp>
    <ErrorCode>0</ErrorCode>
    <ErrorMessage/>
    <Alert>...</Alert>
    <Alert>...</Alert>
    <Alert>...</Alert>
    

    Even though I'm using a for loop it only returns the value I want from the first node and ignores the rest. Am I missing a feature that's not mentioned?

    help wanted question 
    opened by millerrob1000 2
  • How to access declared namespaces?

    How to access declared namespaces?

    I'm trying to understand the preferred way to access declared namespaces. I can get a dictionary of the declared namespaces from the root element's attributes. Is there a more direct way?

    Also, if process namespaces is set to true, I can't find anywhere where the namespace is saved. #34 covers this; am I correct in understanding that the namespace is just removed and discarded?

    help wanted question 
    opened by siegesmund 1
Owner
Marko Tadić
keep it simple
Marko Tadić
Easy XML parsing using Codable protocols in Swift

XMLCoder Encoder & Decoder for XML using Swift's Codable protocols. This package is a fork of the original ShawnMoore/XMLParsing with more features an

Max Desiatov 657 Dec 30, 2022
A fast & lightweight XML & HTML parser in Swift with XPath & CSS support

Fuzi (斧子) A fast & lightweight XML/HTML parser in Swift that makes your life easier. [Documentation] Fuzi is based on a Swift port of Mattt Thompson's

Ce Zheng 994 Jan 2, 2023
A simple way to map XML to Objects written in Swift

XMLMapper XMLMapper is a framework written in Swift that makes it easy for you to convert your model objects (classes and structs) to and from XML. Ex

Giorgos Charitakis 109 Jan 6, 2023
Simple XML Parser implemented in Swift

Simple XML Parser implemented in Swift What's this? This is a XML parser inspired by SwiftyJSON and SWXMLHash. NSXMLParser in Foundation framework is

Yahoo! JAPAN 531 Jan 1, 2023
Fetch a XML feed and parse it into objects

AlamofireXmlToObjects ?? This is now a subspec of EVReflection and the code is maintained there. ?? You can install it as a subspec like this: use_fra

Edwin Vermeer 65 Dec 29, 2020
CheatyXML is a Swift framework designed to manage XML easily

CheatyXML CheatyXML is a Swift framework designed to manage XML easily. Requirements iOS 8.0 or later tvOS 9.0 or later Installation Cocoapods If you'

Louis Bodart 24 Mar 31, 2022
The most swifty way to deal with XML data in swift 5.

SwiftyXML SwiftyXML use most swifty way to deal with XML data. Features Infinity subscript dynamicMemberLookup Support (use $ started string to subscr

Kevin 99 Sep 6, 2022
Ji (戟) is an XML/HTML parser for Swift

Ji 戟 Ji (戟) is a Swift wrapper on libxml2 for parsing XML/HTML. Features Build XML/HTML Tree and Navigate. XPath Query Supported. Comprehensive Unit T

HongHao Zhang 824 Dec 15, 2022
Kanna(鉋) is an XML/HTML parser for Swift.

Kanna(鉋) Kanna(鉋) is an XML/HTML parser for cross-platform(macOS, iOS, tvOS, watchOS and Linux!). It was inspired by Nokogiri(鋸). ℹ️ Documentation Fea

Atsushi Kiwaki 2.3k Dec 31, 2022
A sensible way to deal with XML & HTML for iOS & macOS

Ono (斧) Foundation lacks a convenient, cross-platform way to work with HTML and XML. NSXMLParser is an event-driven, SAX-style API that can be cumbers

Mattt 2.6k Dec 14, 2022
Generate styled SwiftUI Text from strings with XML tags.

XMLText is a mini library that can generate SwiftUI Text from a given XML string with tags. It uses AttributedString to compose the final text output.

null 15 Dec 7, 2022
Mathias Köhnke 1.1k Dec 16, 2022
Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript.

Mongrel is a Swift and HTML hybrid with a bit of support for CSS and Javascript. Using a declaritive style of programming, Mongrel makes writing HTML feel natural and easy. Mongrel also uses a SwiftUI like body structure allowing structs to be completely dedicated as an HTML page or element.

Nicholas Bellucci 12 Sep 22, 2022
SwiftSoup: Pure Swift HTML Parser, with best of DOM, CSS, and jquery (Supports Linux, iOS, Mac, tvOS, watchOS)

SwiftSoup is a pure Swift library, cross-platform (macOS, iOS, tvOS, watchOS and Linux!), for working with real-world HTML. It provides a very conveni

Nabil Chatbi 3.7k Jan 6, 2023
Pick a date and explore websites from the early days of the internet to now all in an easy-to-use browser format! 💻

Pick a date and explore websites from the early days of the internet to now all in an easy-to-use browser format! ??

Liam 5 Nov 26, 2022
📄 A Swift DSL for writing type-safe HTML/CSS in SwiftUI way

?? swift-web-page (swep) Swep is a Swift DSL for writing type-safe HTML/CSS in SwiftUI way. Table of Contents Motivation Examples Safety Design FAQ In

Abdullah Aljahdali 14 Dec 31, 2022
Swift package to convert a HTML table into an array of dictionaries.

Swift package to convert a HTML table into an array of dictionaries.

null 1 Jun 18, 2022
A lightweight XMLParser for assembling and parsing XML values written for iOS 8+ in Swift 2.

Overview Description Requirements Installation Usage Author License Description XMLParser lets you convert a pure Swift dictionary into XML string and

Eugene Mozharovsky 75 Feb 2, 2022
Simple XML parsing in Swift

SWXMLHash SWXMLHash is a relatively simple way to parse XML in Swift. If you're familiar with NSXMLParser, this library is a simple wrapper around it.

David Mohundro 1.3k Jan 3, 2023
Super awesome Swift minion for Core Data (iOS, macOS, tvOS)

⚠️ Since this repository is going to be archived soon, I suggest migrating to NSPersistentContainer instead (available since iOS 10). For other conven

Marko Tadić 306 Sep 23, 2022