Simple XML Parser implemented in Swift

Overview

swiftyxmlparserlogo

Swift 5.0 Carthage compatible Version License Platform

Simple XML Parser implemented in Swift

What's this?

This is a XML parser inspired by SwiftyJSON and SWXMLHash.

NSXMLParser in Foundation framework is a kind of "SAX" parser. It has enough performance but is a little inconvenient. So we have implemented "DOM" parser wrapping it.

Feature

  • access XML Document with "subscript".
  • access XML Document as Sequence.
  • easy debugging XML pathes.

Requirement

  • iOS 9.0+
  • tvOS 9.0+
  • macOS 10.10+
  • Swift 5.0

Installation

Carthage

1. create Cartfile

github "https://github.com/yahoojapan/SwiftyXMLParser"

2. install

> carthage update

CocoaPods

1. create Podfile

platform :ios, '9.0'
use_frameworks!

pod "SwiftyXMLParser", :git => 'https://github.com/yahoojapan/SwiftyXMLParser.git'

2. install

> pod install

Example

import SwiftyXMLParser

let str = """
<ResultSet>
    <Result>
        <Hit index=\"1\">
            <Name>Item1</Name>
        </Hit>
        <Hit index=\"2\">
            <Name>Item2</Name>
        </Hit>
    </Result>
</ResultSet>
"""

// parse xml document
let xml = try! XML.parse(str)

// access xml element
let accessor = xml["ResultSet"]

// access XML Text

if let text = xml["ResultSet", "Result", "Hit", 0, "Name"].text {
    print(text)
}

if let text = xml.ResultSet.Result.Hit[0].Name.text {
    print(text)
}

// access XML Attribute
if let index = xml["ResultSet", "Result", "Hit", 0].attributes["index"] {
    print(index)
}

// enumerate child Elements in the parent Element
for hit in xml["ResultSet", "Result", "Hit"] {
    print(hit)
}

// check if the XML path is wrong
if case .failure(let error) =  xml["ResultSet", "Result", "TypoKey"] {
    print(error)
}

Usage

1. Parse XML

  • from String
let str = """
<ResultSet>
    <Result>
        <Hit index=\"1\">
            <Name>Item1</Name>
        </Hit>
        <Hit index=\"2\">
            <Name>Item2</Name>
        </Hit>
    </Result>
</ResultSet>
"""

xml = try! XML.parse(str) // -> XML.Accessor
  • from NSData
let str = """
<ResultSet>
    <Result>
        <Hit index=\"1\">
            <Name>Item1</Name>
        </Hit>
        <Hit index=\"2\">
            <Name>Item2</Name>
        </Hit>
    </Result>
</ResultSet>
"""

let string = String(decoding: data, as: UTF8.self)

xml = XML.parse(data) // -> XML.Accessor
  • with invalid character
let srt = "<xmlopening>@ß123\u{1c}</xmlopening>"

let xml = XML.parse(str.data(using: .utf8))

if case .failure(XMLError.interruptedParseError) = xml {
  print("invalid character")
}

For more, see https://developer.apple.com/documentation/foundation/xmlparser/errorcode

2. Access child Elements

let element = xml.ResultSet // -> XML.Accessor

3. Access grandchild Elements

  • with String
let element = xml["ResultSet"]["Result"] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
  • with Array
let path = ["ResultSet", "Result"]
let element = xml[path] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
  • with Variadic
let element = xml["ResultSet", "Result"] // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>
  • with @dynamicMemberLookup
let element = xml.ResultSet.Result // -> <Result><Hit index=\"1\"><Name>Item1</Name></Hit><Hit index=\"2\"><Name>Item2</Name></Hit></Result>

4. Access specific grandchild Element

let element = xml.ResultSet.Result.Hit[1] // -> <Hit index=\"2\"><Name>Item2</Name></Hit>

5. Access attribute in Element

if let attributeValue = xml.ResultSet.Result.Hit[1].attributes?["index"] {
  print(attributeValue) // -> 2
}

6. Access text in Element

  • with optional binding
if let text = xml.ResultSet.Result.Hit[1].Name.text {
    print(text) // -> Item2
} 
  • with custom operation
struct Entity {
  var name = ""
}
let entity = Entity()
entity.name ?= xml.ResultSet.Result.Hit[1].Name.text // assign if it has text
  • convert Int and assign
struct Entity {
  var name: Int = 0
}
let entity = Entity()
entity.name ?= xml.ResultSet.Result.Hit[1].Name.int // assign if it has Int

and there are other syntax sugers, bool, url and double.

  • assign text into Array
struct Entity {
  var names = [String]()
}
let entity = Entity()
entity.names ?<< xml.ResultSet.Result.Hit[1].Name.text // assign if it has text

7. Access CDATA

let str = """
<Data name="DATE">
    <value><![CDATA[2018-07-08]]></value>
</Data>
"""

// parse xml document
let xml = try! XML.parse(str)
        
if let cdata = xml.Data.value.element?.CDATA, 
   let cdataStr = String(data: cdata, encoding: .utf8) {
   print(cdataStr) // -> "2018-07-08"
}

7. Count child Elements

let numberOfHits = xml.ResultSet.Result.Hit.all?.count 

8. Check error

print(xml.ResultSet.Result.TypoKey) // -> "TypoKey not found."

9. Access as SequenceType

  • for-in
for element in xml.ResultSet.Result.Hit {
  print(element.text)
}
  • map
xml.ResultSet.Result.Hit.map { $0.Name.text }

10. Generate XML document

print(Converter(xml.ResultSet).makeDocument())

Work with Alamofire

SwiftyXMLParser goes well with Alamofire. You can parse the response easily.

import Alamofire
import SwiftyXMLParser

Alamofire.request(.GET, "https://itunes.apple.com/us/rss/topgrossingapplications/limit=10/xml")
         .responseData { response in
            if let data = response.data {
                let xml = XML.parse(data)
                print(xml.feed.entry[0].title.text) // outputs the top title of iTunes app raning.
            }
        }

In addition, there is the extension of Alamofire to combine with SwiftyXMLParser.

Migration Guide

Current master branch is supporting Xcode10. If you wanna use this library with legacy swift version, read release notes and install the last compatible version.

License

This software is released under the MIT License, see LICENSE.

Comments
  • Compiles with errors in Xcode 9 (Swift version 3.2)

    Compiles with errors in Xcode 9 (Swift version 3.2)

    Compiling the project with Xcode 9 using Swift version 3.2, I get the following errors in Accessor.swift inlined below:

            public func makeIterator() -> AnyIterator<Accessor> {
                let generator: [Element]
                switch self {
                case .failure(_):
                    generator = [Element]()
                case .singleElement(let element):
                    generator = [element] // Cannot convert value of type 'XML.Element' to expected element type 'XML.Accessor'
                case .sequence(let elements):
                    generator = elements // Cannot assign value of type '[XML.Element]' to type '[XML.Accessor.Element]' (aka 'Array<XML.Accessor>')
                }
                var index = 0
                return AnyIterator {
                    let nextAccessor: Accessor?
                    if index < generator.count {
                        nextAccessor = Accessor(generator[index]) // Cannot invoke initializer for type 'XML.Accessor' with an argument list of type '(XML.Accessor.Element)'
                        index += 1
                    } else {
                        nextAccessor = nil
                    }
                    return nextAccessor
                }
            }
    
    

    This one is beyond me. Does anyone know how we can fix this?

    EDIT: Just confirmed with Apple engineer @WWDC that this could be a bug where the type inference is incorrect. To workaround:

            public func makeIterator() -> AnyIterator<Accessor> {
                let generator: [XML.Element]
                switch self {
                case .failure(_):
                    generator = []
                    .
                    .
           }
    

    I can not confirm if this establishes the required behaviour for SwiftyXMLParser.

    EDIT: I ran the unit tests on the project and it passes.

    opened by michaeltansg 13
  • Illegal instruction: 4

    Illegal instruction: 4

    I'm using Xcode 11.5 & Swift 5.2.4. Trying to parse xml like:

    let buffer = """
    <_otherElement>…</_otherElement>
    <_elementIneed>…</_elementIneed>
    <_otherElement>…</_otherElement>
    <_otherElement>…</_otherElement>
    <_elementIneed>…</_elementIneed>
    <_otherElement>…</_otherElement>
    <_elementIneed>…</_elementIneed>
    """
    

    I want to access sequence ("_elementIneed"), but project doesn't get compiled and Xcode suggests to report a bug.

    var xml: XML.Accessor!
    xml = XML.parse(buffer)
    xml._elementIneed.makeIterator() //<---
    xml._elementIneed.flatMap { $0 } //<---
    xml._elementIneed.first //<---
    xml._elementIneed.last //<---
    
    xml._elementIneed.map { $0 } // ok
    
    opened by bigtrade2017 6
  • 5.4.0 CDATA change breaks my parsing

    5.4.0 CDATA change breaks my parsing

    Hi, see this extract from my XML file:

                <Data name="DATE">
                    <value><![CDATA[2018-07-08]]></value>
                </Data>
    

    Up to v5.3.0 I could parse the date like this:

                        if let dateString = data.value.text {
                            ... // dateString == "2018-07-08"
                        }
    

    But in 5.4.0 data.value.text is nil. Should I change the parsing code when using 5.4.0?

    opened by BirdArch 3
  • Add support for CDATA

    Add support for CDATA

    Hi, I want to be able to add HTML to my XML file but it can't parse it anymore. Adding CDATA elements fixes this but this framework doesn't seem to support that. I've had to use Base64 instead but that uses more storage and processing so hopefully this can be improved. Thanks

    opened by EthanLipnik 3
  • Feature: Set text, append element and ignore namespaces

    Feature: Set text, append element and ignore namespaces

    This PR covers the following 3 features. Might have been better to split it in 3 pull requests, but let us see if we can go through them one by one and I will make the necessary adjustments:

    Feature 1: Ability to get and set text and attributes on single element using the Accessor, and keep the modified elements when creating an xml document. The same small change could also be applied to other types than text/string. It requires that the accessor is not created as a constant.

    
    var accessor = XML.Accessor(singleElement())
    accessor.text = "text2"
    XCTAssertEqual(accessor.text, "text2", "set text on first single element")
    

    Feature 2: Ability to append a new element to an accessor.

    let accessor = XML.Accessor(singleElement())
    accessor.append(singleElement())
    XCTAssertEqual(accessor["RootElement"].text, "text", "check newly added element")
    

    Feature 3: Global flag for ignoring namespaces when accessing elements. Currently I am using this framework with SOAP XML, which has dynamically prefixed namespaces on all elements.

    <?xml version=\"1.0\" encoding=\"UTF-8\"?>
    <env:RootElement key=\"value\">
      <ns1:ChildElement>childText1</ns1:ChildElement>
      <ns2:ChildElement>childText2</ns2:ChildElement>
    </env:RootElement>
    
    let accessor = XML.Accessor(xmlFromAbove())
    XML.ignoreNamespaces = true
    XCTAssertEqual(accessor["ChildElement"].first.text, "childText1", "access text for first child element ignoring namespace")
    

    Bugfix 1: XML documents are created with an extra space when no attributes are added for an element. Bugfix 2: XML declaration prefix is not optional. Bugfix 3: XML Element does not have a full initialiser.

    All 3 features have corresponding unit tests. Let me know what you think.

    opened by mrotrifork 2
  • Store line numbers and CDATA on Element

    Store line numbers and CDATA on Element

    I added three new properties: Element.lineNumberStart, Element.lineNumberEnd, and Element.CDATA. I'm building a tool that reports errors in XML files and I need to be able to report the line number so the user can find the error.

    I also made some changes that make the tests run in a Swift Package Manager context. I believe they will continue to work as they were before; the only difference is I'm using a path-based lookup based on the unit test file instead of Bundle.

    opened by stevelandeyasana 2
  • `SwiftyXMLParser` does not specify a Swift version

    `SwiftyXMLParser` does not specify a Swift version

    Hi there,

    Error message: - SwiftyXMLParser does not specify a Swift version and none of the targets (UnityFramework) integrating it have the SWIFT_VERSION attribute set. Please contact the author or set the SWIFT_VERSION attribute in at least one of the targets that integrate this pod.

    This becomes a real issue for us. Especially when the project files are auto-generated on certain platforms e.g. Unity.

    The solution is to set the swift_version in the podspec file which should be strait forward. https://guides.cocoapods.org/syntax/podspec.html

    opened by gunhansancar 2
  • Problem with parsing XML!

    Problem with parsing XML!

    Hello! I use SwiftyXMLParser in conjunction with Alamofier. I need to parse XML data from the news feed, for example lenta.ru/rss. The problem is in this piece of code, on line 7:

    1 AF.request (url) .response { (response) in 2              if let data = response.data { 3                 let xml = XML.parse (data) 4                 var itemNumber = 0 5                 while xml.rss.channel.item [itemNumber] .text! = nil { 6                     self.newsModel = NewsModel () 7                     self.newsModel.newsDescription = xml.rss.channel.item[itemNumber].description.text ?? "" 8                     self.models.append (self.newsModel) 9                     itemNumber + = 1 10                 } 11                 completion (self.models) 12             } 13 }

    "description" in line 7 is a complete description of the news, but this parameter is reserved in your API. Is there any way around this point?

    opened by MagDA1906 2
  • Swift Package Manager support

    Swift Package Manager support

    Due apple introduced SPM support natively in Xcode 11, will the project be updated to SPM support? And should we expect it soon or it's long term, low priority task ?

    opened by RomaSafin 2
  • How can get the raw XML?

    How can get the raw XML?

    Hi,

    a couple of people have asked the same question, but I don't think there has been a satisfactory answer, unless I've missed it.

    Lets say I want to parse some XML : AWS EC2 API

    I get the results back and do something like this let instances = XML.parse(response)

    Now I want to iterate around the returned objects so I do this:

    for instance in instances["DescribeInstancesResponse", "reservationSet", "item", "instancesSet", "item"] {
    
    }
    

    What I want to do is store the information in Core Data, and because the elements of the XML Instance Item could change over time all I'm going to do is put the whole XML into the database so I don't need to worry about constantly updating the Managed Object model.

    How can I get the original XML for the instance variable from the above example?

    • I've tried instance.text - this just returns a lot of "\r"
    • I've tried this print(String(data: data, encoding: .utf8)!) but I'm told that XML.Access is not convertible to Data

    Is there a way of getting XML from an Accessor?

    Thanks,

    Mark

    opened by haskins-io 2
  • Unicode Characters

    Unicode Characters

    While using SwiftyXMLParser with a string with unicode characters inside, the parser automatically stops at the (probably) unknown message.

    Inside Message was something like this: <xmlopening>@ß123\u{1c}</xmlopening>

    I think the \u{1c} is a character caused by application / data transfer error.

    After this line in file, the rest of XML will be ignored.

    opened by NIESolutions 2
  • how to handle namespaces

    how to handle namespaces

    I have an rdf XML that uses namespaces. Can this be handled? For example

    <rdf:tag xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> rdf:tag2SomeValue</rdf:tag2> </rdf:tag>

    I understand that I can use xml = xml.parser(str) and then access via xml["rdf:tag2] but I don't believe you can use the xml.rdf:tag2 notation. How would I be able to access using the second approach?

    opened by TheApApp 0
  • Fix warning when using from extension

    Fix warning when using from extension

    Issue: The following warning is displayed when using SwiftyXMLParser from the extension. warning: linking against a dylib which is not safe for use in application extensions:

    Fix: I have enabled "Require Only App-Extension-Safe API" in the build settings. image

    opened by shtnkgm 0
  • Is it able to replace or modify an element?

    Is it able to replace or modify an element?

    For example, I have this one:

    <a>
        <b something="1">This is a test</b>
    </a>
    

    Is it possible to replace <b> to <c>?

    <a>
        <c somethingelse="2">That is a test</b>
    </a>
    
    opened by zhouhao27 1
  • Help needed

    Help needed

    Hi,

    Framework seems great! Thank you!

    I need some help. I'd like to parse an iTunes API request (top 20 albums: https://itunes.apple.com/FR/rss/topalbums/limit=20/xml) and save in a datasource:

    For each album:

    • link
    • title
    • name
    • artist
    • image url

    Can you please provide an example?

    Regards,

    opened by iDevelopper 1
  • Getting Illegal instruction: 4 error while using map or for loop

    Getting Illegal instruction: 4 error while using map or for loop

    Hi, I am trying to get the array of the xml elements by using the map / flatmap or for loop but I am getting Illegal instruction: 4 error.

    let b = xml["S:Envelope", "S:Body", "ModelArrayElement", "model"].map { $0.Name.text }

    opened by balasaida 2
Releases(5.6.0)
Owner
Yahoo! JAPAN
Yahoo! JAPAN
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
Swift minion for simple and lightweight XML parsing

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

Marko Tadić 975 Dec 26, 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
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
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
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 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
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
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
Mathias Köhnke 1.1k Dec 16, 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
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
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
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
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