Simple XML parsing in Swift

Overview

SWXMLHash

CocoaPods Carthage compatible CocoaPods Join the chat at https://gitter.im/drmohundro/SWXMLHash codebeat

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. Conceptually, it provides a translation from XML to a dictionary of arrays (aka hash).

The API takes a lot of inspiration from SwiftyJSON.

Contents

Requirements

  • iOS 8.0+ / Mac OS X 10.9+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.0+

Installation

SWXMLHash can be installed using CocoaPods, Carthage, Swift Package Manager, or manually.

CocoaPods

To install CocoaPods, run:

$ gem install cocoapods

Then create a Podfile with the following contents:

platform :ios, '10.0'
use_frameworks!

target 'YOUR_TARGET_NAME' do
  pod 'SWXMLHash', '~> 5.0.0'
end

Finally, run the following command to install it:

$ pod install

Carthage

To install Carthage, run (using Homebrew):

$ brew update
$ brew install carthage

Then add the following line to your Cartfile:

github "drmohundro/SWXMLHash" ~> 5.0

Swift Package Manager

Swift Package Manager requires Swift version 4.0 or higher. First, create a Package.swift file. It should look like:

dependencies: [
    .package(url: "https://github.com/drmohundro/SWXMLHash.git", from: "5.0.0")
]

swift build should then pull in and compile SWXMLHash for you to begin using.

Manual Installation

To install manually, you'll need to clone the SWXMLHash repository. You can do this in a separate directory or you can make use of git submodules - in this case, git submodules are recommended so that your repository has details about which commit of SWXMLHash you're using. Once this is done, you can just drop the SWXMLHash.swift file into your project.

NOTE: if you're targeting iOS 7, you'll have to install manually because embedded frameworks require a minimum deployment target of iOS 8 or OSX Mavericks.

Getting Started

If you're just getting started with SWXMLHash, I'd recommend cloning the repository down and opening the workspace. I've included a Swift playground in the workspace which makes it easy to experiment with the API and the calls.

Swift Playground

Configuration

SWXMLHash allows for limited configuration in terms of its approach to parsing. To set any of the configuration options, you use the configure method, like so:

let xml = SWXMLHash.config {
              config in
              // set any config options here
          }.parse(xmlToParse)

The available options at this time are:

  • shouldProcessLazily
    • This determines whether not to use lazy loading of the XML. It can significantly increase the performance of parsing if your XML is large.
    • Defaults to false
  • shouldProcessNamespaces
    • This setting is forwarded on to the internal NSXMLParser instance. It will return any XML elements without their namespace parts (i.e. "<h:table>" will be returned as "<table>")
    • Defaults to false
  • caseInsensitive
    • This setting allows for key lookups to be case insensitive. Typically XML is a case sensitive language, but this option lets you bypass this if necessary.
    • Defaults to false
  • encoding
    • This setting allows for explicitly specifying the character encoding when an XML string is passed to parse.
    • Defaults to String.encoding.utf8
  • userInfo
    • This setting mimics Codable's userInfo property to allow the user to add contextual information that will be used for deserialization.
    • See Codable's userInfo docs
    • The default is [:]
  • detectParsingErrors
    • This setting attempts to detect XML parsing errors. parse will return an XMLIndexer.parsingError if any parsing issues are found.
    • Defaults to false (because of backwards compatibility and because many users attempt to parse HTML with this library)

Examples

All examples below can be found in the included specs.

Initialization

let xml = SWXMLHash.parse(xmlToParse)

Alternatively, if you're parsing a large XML file and need the best performance, you may wish to configure the parsing to be processed lazily. Lazy processing avoids loading the entire XML document into memory, so it could be preferable for performance reasons. See the error handling for one caveat regarding lazy loading.

let xml = SWXMLHash.config {
              config in
              config.shouldProcessLazily = true
          }.parse(xmlToParse)

The above approach uses the new config method, but there is also a lazy method directly off of SWXMLHash.

let xml = SWXMLHash.lazy(xmlToParse)

Single Element Lookup

Given:

<root>
  <header>
    <title>Foo</title>
  </header>
  ...
</root>

Will return "Foo".

xml["root"]["header"]["title"].element?.text

Multiple Elements Lookup

Given:

<root>
  ...
  <catalog>
    <book><author>Bob</author></book>
    <book><author>John</author></book>
    <book><author>Mark</author></book>
  </catalog>
  ...
</root>

The below will return "John".

xml["root"]["catalog"]["book"][1]["author"].element?.text

Attributes Usage

Given:

<root>
  ...
  <catalog>
    <book id="1"><author>Bob</author></book>
    <book id="123"><author>John</author></book>
    <book id="456"><author>Mark</author></book>
  </catalog>
  ...
</root>

The below will return "123".

xml["root"]["catalog"]["book"][1].element?.attribute(by: "id")?.text

Alternatively, you can look up an element with specific attributes. The below will return "John".

xml["root"]["catalog"]["book"].withAttribute("id", "123")["author"].element?.text

Returning All Elements At Current Level

Given:

<root>
  ...
  <catalog>
    <book><genre>Fiction</genre></book>
    <book><genre>Non-fiction</genre></book>
    <book><genre>Technical</genre></book>
  </catalog>
  ...
</root>

The all method will iterate over all nodes at the indexed level. The code below will return "Fiction, Non-fiction, Technical".

", ".join(xml["root"]["catalog"]["book"].all.map { elem in
    elem["genre"].element!.text!
})

You can also iterate over the all method:

for elem in xml["root"]["catalog"]["book"].all {
    print(elem["genre"].element!.text!)
}

Returning All Child Elements At Current Level

Given:

<root>
  <catalog>
    <book>
      <genre>Fiction</genre>
      <title>Book</title>
      <date>1/1/2015</date>
    </book>
  </catalog>
</root>

The below will print "root", "catalog", "book", "genre", "title", and "date" (note the children method).

func enumerate(indexer: XMLIndexer) {
    for child in indexer.children {
        print(child.element!.name)
        enumerate(child)
    }
}

enumerate(indexer: xml)

Filtering elements

Given:

<root>
  <catalog>
    <book id=\"bk101\">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre><price>44.95</price>
      <publish_date>2000-10-01</publish_date>
    </book>
    <book id=\"bk102\">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
    </book>
    <book id=\"bk103\">
      <author>Corets, Eva</author>
      <title>Maeve Ascendant</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-11-17</publish_date>
    </book>
  </catalog>
</root>

The following will return return "Midnight Rain". Filtering can be by any part of the XMLElement class or by index as well.

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)

Error Handling

Using Swift 2.0's new error handling feature:

do {
    try xml!.byKey("root").byKey("what").byKey("header").byKey("foo")
} catch let error as IndexingError {
    // error is an IndexingError instance that you can deal with
}

Or using the existing indexing functionality:

switch xml["root"]["what"]["header"]["foo"] {
case .element(let elem):
    // everything is good, code away!
case .xmlError(let error):
    // error is an IndexingError instance that you can deal with
}

Note that error handling as shown above will not work with lazy loaded XML. The lazy parsing doesn't actually occur until the element or all method are called - as a result, there isn't any way to know prior to asking for an element if it exists or not.

Simple Type Conversion

Given:

<root>
  <elem>Monday, 23 January 2016 12:01:12 111</elem>
</root>

With the following implementation for Date element and attribute deserialization:

extension Date: XMLElementDeserializable, XMLAttributeDeserializable {
    public static func deserialize(_ element: 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
    }

    private static func stringToDate(_ dateAsString: String) -> Date? {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
        return dateFormatter.date(from: dateAsString)
    }
}

The below will return a date value:

let dt: Date = try xml["root"]["elem"].value()

Complex Types Conversion

Given:

<root>
  <books>
    <book isbn="0000000001">
      <title>Book A</title>
      <price>12.5</price>
      <year>2015</year>
      <categories>
        <category>C1</category>
        <category>C2</category>
      </categories>
    </book>
    <book isbn="0000000002">
      <title>Book B</title>
      <price>10</price>
      <year>1988</year>
      <categories>
        <category>C2</category>
        <category>C3</category>
      </categories>
    </book>
    <book isbn="0000000003">
      <title>Book C</title>
      <price>8.33</price>
      <year>1990</year>
      <amount>10</amount>
      <categories>
        <category>C1</category>
        <category>C3</category>
      </categories>
    </book>
  </books>
</root>

with Book struct implementing XMLIndexerDeserializable:

struct Book: XMLIndexerDeserializable {
    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()
        )
    }
}

The below will return an array of Book structs:

let books: [Book] = try xml["root"]["books"]["book"].value()

Types Conversion

You can convert any XML to your custom type by implementing XMLIndexerDeserializable for any non-leaf node (e.g. <book> in the example above).

For leaf nodes (e.g. <title> in the example above), built-in converters support Int, Double, Float, Bool, and String values (both non- and -optional variants). Custom converters can be added by implementing XMLElementDeserializable.

For attributes (e.g. isbn= in the example above), built-in converters support the same types as above, and additional converters can be added by implementing XMLAttributeDeserializable.

Types conversion supports error handling, optionals and arrays. For more examples, look into SWXMLHashTests.swift or play with types conversion directly in the Swift playground.

FAQ

Does SWXMLHash handle URLs for me?

No - SWXMLHash only handles parsing of XML. If you have a URL that has XML content on it, I'd recommend using a library like AlamoFire to download the content into a string and then parsing it.

Does SWXMLHash support writing XML content?

No, not at the moment - SWXMLHash only supports parsing XML (via indexing, deserialization, etc.).

I'm getting an "Ambiguous reference to member 'subscript'" when I call .value().

.value() is used for deserialization - you have to have something that implements XMLIndexerDeserializable (or XMLElementDeserializable if it is a single element versus a group of elements) and that can handle deserialization to the left-hand side of expression.

For example, given the following:

let dateValue: Date = try! xml["root"]["date"].value()

You'll get an error because there isn't any built-in deserializer for Date. See the above documentation on adding your own deserialization support. In this case, you would create your own XMLElementDeserializable implementation for Date. See above for an example of how to add your own Date deserialization support.

I'm getting an EXC_BAD_ACCESS (SIGSEGV) when I call parse()

Chances are very good that your XML content has what is called a "byte order mark" or BOM. SWXMLHash uses NSXMLParser for its parsing logic and there are issues with it and handling BOM characters. See issue #65 for more details. Others who have run into this problem have just stripped the BOM out of their content prior to parsing.

How do I handle deserialization with a class versus a struct (such as with NSDate)?

Using extensions on classes instead of structs can result in some odd catches that might give you a little trouble. For example, see this question on StackOverflow where someone was trying to write their own XMLElementDeserializable for NSDate which is a class and not a struct. The XMLElementDeserializable protocol expects a method that returns Self - this is the part that gets a little odd.

See below for the code snippet to get this to work and note in particular the private static func value<T>() -> T line - that is the key.

extension NSDate: XMLElementDeserializable {
    public static func deserialize(_ element: XMLElement) throws -> Self {
        guard let dateAsString = element.text else {
            throw XMLDeserializationError.nodeHasNoValue
        }

        let dateFormatter = NSDateFormatter()
        dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
        let date = dateFormatter.dateFromString(dateAsString)

        guard let validDate = date else {
            throw XMLDeserializationError.typeConversionFailed(type: "Date", element: element)
        }

        // NOTE THIS
        return value(validDate)
    }

    // AND THIS
    private static func value<T>(date: NSDate) -> T {
        return date as! T
    }
}

How do I handle deserialization with an enum?

Check out this great suggestion/example from @woolie up at https://github.com/drmohundro/SWXMLHash/discussions/245.

Have a different question?

Feel free to shoot me an email, post a question on StackOverflow, or open an issue if you think you've found a bug. I'm happy to try to help!

Another alternative is to post a question in the Discussions.

Changelog

See CHANGELOG for a list of all changes and their corresponding versions.

Contributing

See CONTRIBUTING for guidelines to contribute back to SWXMLHash.

License

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

Comments
  • Add automatic deserialization of attributes.

    Add automatic deserialization of attributes.

    Recent pull request #68 from @ncreated introduced automatic type conversion for Elements and Indexers, but working with attributes is still painful. I am working with some (imo) poorly designed XML, which has a number of elements like this:

    <stats minplayers="2" maxplayers="2" minplaytime="40" maxplaytime="60" playingtime="60" numowned="253" rating="8.5">

    It would be nice to be able to automatically convert these attributes into Int, Float, Double, Bool, and String types (as well as Int?, Float?, etc...) with a syntax similar to node["test"].value().

    As it stands now, when processing attributes within a deserializer, I need to do crazy optional gymnastics, like this:

    var minPlayers: Int
    if let minPlayersString = node["stats"].attribute("minplayers") {
       if let minPlayersInt = Int(minPlayersString) {
          minPlayers = minPlayersInt
       }
    }
    

    I would love to be able to replace this with something like this: var minPlayers: Int = node["stats"].attribute("minplayers")

    Swift's 'guard' syntax makes this slightly less laborious if the values are guaranteed to be there, but there is still certainly room for improvement.

    enhancement 
    opened by gca3020 33
  • This node is invalid:

    This node is invalid:

    I'm fetching Google Contacts and I'm trying to Deserialize the object using 'SWXMLHash' but when I try to Deserialize it gives error

    This node is invalid:

    Data format

    <feed gd:etag="W/"DEENSH0_fit7I2A9XR9QEk8.""
        xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
        xmlns="http://www.w3.org/2005/Atom"
        xmlns:gContact="http://schemas.google.com/contact/2008"
        xmlns:batch="http://schemas.google.com/gdata/batch"
        xmlns:gd="http://schemas.google.com/g/2005">
        <id>[email protected]</id>
        <updated>2017-04-26T17:11:39.346Z</updated>
        <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"></category>
        <title>Nitesh's Contacts</title>
        <link rel="alternate" type="text/html" href="http://www.google.com/"></link>
        <link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin"></link>
        <link rel="http://schemas.google.com/g/2005#post" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin"></link>
        <link rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin/batch"></link>
        <link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin?max-results=10000"></link>
        <author>
            <name>Nitesh</name>
            <email>[email protected]</email>
        </author>
        <generator uri="http://www.google.com/m8/feeds" version="1.0">Contacts</generator>
        <openSearch:totalResults>4797</openSearch:totalResults>
        <openSearch:startIndex>1</openSearch:startIndex>
        <openSearch:itemsPerPage>10000</openSearch:itemsPerPage>
        <entry gd:etag=""Q3wzeDsadssaVSLysdadst7I2Adas9XRVaEk8PRAI."">
            <id>http://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/base/16adsadsdasaa90ce88927</id>
            <updated>2015-08-10T16:49:22.280Z</updated>
            <app:edited
                xmlns:app="http://www.w3.org/2007/app">2015-08-10T16:49:22.280Z
            
            
            </app:edited>
            <category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/contact/2008#contact"></category>
            <title>Bhise</title>
            <link rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*" href="https://www.google.com/m8/feeds/photos/media/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/16aaa90ce88927"></link>
            <link rel="self" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin/16aaa90ce88927"></link>
            <link rel="edit" type="application/atom+xml" href="https://www.google.com/m8/feeds/contacts/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/thin/16aaa90ce88927"></link>
            <gd:name>
                <gd:fullName>Bhise</gd:fullName>
                <gd:givenName>Bhise</gd:givenName>
            </gd:name>
            <gd:organization rel="http://schemas.google.com/g/2005#work">
                <gd:orgName>Ref:kedar</gd:orgName>
            </gd:organization>
            <gd:email rel="http://schemas.google.com/g/2005#other" address="[email protected]" primary="true"></gd:email>
            <gd:phoneNumber rel="http://schemas.google.com/g/20das05#mobile" uri="tel:+91-9922dsd9-26631">+91 99-22-926631</gd:phoneNumber>
            <gContact:groupMembershipInfo deleted="false" href="http://www.google.com/m8/feeds/groups/cdsadsdsoolndasdsdidsadtmadasns%40gmail.com/base/6"></gContact:groupMembershipInfo>
        </entry>
    </feed>
    

    GoogleContactsDeserializable File

    struct GoogleContacts: XMLIndexerDeserializable {
        let fullName: String
        let email: String
    
        static func deserialize(_ node: XMLIndexer) throws -> GoogleContacts {
            return try GoogleContacts(
                fullName: node["gd:name"]["gd:fullName"].value(),
                email: node["gd:email"].value(ofAttribute: "address")
            )
        }
    }
    

    ViewController

    let xml = SWXMLHash.parse(data)
    do {
        let nodes: [GoogleContacts] = try xml["feed"]["entry"].value()
        print(nodes)
    } catch let error {
        print(error)
    }
    
    question 
    opened by n1tesh 18
  • Async NSXML can’t work in sync mode, please add completion handlers

    Async NSXML can’t work in sync mode, please add completion handlers

    Sometimes “parse” method returns an empty object (every 4-5th execution)

    Code: let xml = SWXMLHash.parse(data)

    Log: (lldb) po xml Element { Element = 0x00007f93f2e18960 { name = "SWXMLHash_Root_Element" text = nil attributes = {} children = 0 values {} count = 0 index = 0 } }

    (lldb) po data <OS_dispatch_data: data[0x7f93f2e29830] = { composite, size = 2031, num_records = 2 record[0] = { from = 0, length = 1297, data_object = 0x7f93f2ccdff0 }, record[1] = { from = 0, length = 734, data_object = 0x7f93f2e157c0 }, }>

    need repro awaiting reply 
    opened by alusev 14
  • Add support for Swift 3 and Xcode 8 support

    Add support for Swift 3 and Xcode 8 support

    At the moment, SWXMLHash itself compiles, but the tests won't compile without a corresponding Swift 3 version of both Quick and Nimble.

    This PR will also be what ships in SWXMLHash version 3.0.0.

    pre-release 
    opened by drmohundro 13
  • Getting entire HTML between opening and closing tag.

    Getting entire HTML between opening and closing tag.

    My question might be a little bit lame but I just can't figure it out. How to get everything between opening and closing html tag? For example now I have let b = xml["everything"]["news"][i]["content"].element?.text which returns string: Click this for super cool news. and I would like it to return Click this <a href="https://www.link.com">link</a> for super cool news. Thank you for help.

    bug help wanted question 
    opened by Pawel-Janik 13
  • Carthage using builds unnecessary dependencies

    Carthage using builds unnecessary dependencies

    Nimble and Quick dependencies are builded too, because they have shared scheme in project. But I want to use SWXMLHash and Nimble or Quick are worthless for me. And it takes some time when more frameworks is builded.

    bug 
    opened by tomassliz 12
  • lazily create nested enums

    lazily create nested enums

    Reading https://devforums.apple.com/message/1101244#1101244 it makes me think that this would be the right way to create the nested enum structure lazily.

    Any thoughts?

    enhancement 
    opened by tkrajacic 12
  • Getting Attribute count to add values to an array in Swift with SWXMLHash

    Getting Attribute count to add values to an array in Swift with SWXMLHash

    Here is the XML I'm parsing with SWXMLHash:

    <response version="1.2" xsi:noNamespaceSchemaLocation="http://aviationweather.gov/adds/schema/metar1_2.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XML-Schema-instance">'
        <request_index>7394484</request_index>
        <data_source name="metars"/>
        <request type="retrieve"/>
        <errors/>
        <warnings/>
        <time_taken_ms>4</time_taken_ms>
        <data num_results="1">
            <METAR>
                <raw_text>KDEN 261153Z 07012KT 3SM -FZDZSN BKN004 OVC008 M05/M06 A3009 RMK AO2 SLP213 P0000 60000 I1000 I6000 T10501061 11039 21050 53009</raw_text>
                <station_id>KDEN</station_id>
                <observation_time>2015-11-26T11:53:00Z</observation_time>
                <latitude>39.85</latitude>
                <longitude>-104.65</longitude>
                <temp_c>-5.0</temp_c>
                <dewpoint_c>-6.1</dewpoint_c>
                <wind_dir_degrees>70</wind_dir_degrees>
                <wind_speed_kt>12</wind_speed_kt>
                <visibility_statute_mi>3.0</visibility_statute_mi>
                <altim_in_hg>30.088583</altim_in_hg>
                <sea_level_pressure_mb>1021.3</sea_level_pressure_mb>
                <quality_control_flags>
                    <auto_station>TRUE</auto_station>
                </quality_control_flags>
                <wx_string>-FZDZ SN</wx_string>
                <sky_condition cloud_base_ft_agl="400" sky_cover="BKN"/>
                <sky_condition cloud_base_ft_agl="800" sky_cover="OVC"/>
                <flight_category>LIFR</flight_category>
                <three_hr_pressure_tendency_mb>0.9</three_hr_pressure_tendency_mb>
                <maxT_c>-3.9</maxT_c>
                <minT_c>-5.0</minT_c>
                <precip_in>0.005</precip_in>
                <metar_type>SPECI</metar_type>
                <elevation_m>1640.0</elevation_m>
            </METAR>
        </data>
    </response> 
    

    I want to get the count of sky_condition sky_cover sky_base_ft_agl as there is always at least one with no cloud base (CLR) or multiple FEW, BKN or OVC with cloud bases with each. I want to be able to get the number of sky conditions and then enumerate through them and add them to an array so that I can show them on a label together.

    Here is the code I have to get the data to parse and then present to a label:

     func getWxData () {
    
            let urlStringMax = "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString=KDEN"
    
        if let urlMax = NSURL(string: urlStringMax) {
    
            if let data = try? NSData(contentsOfURL: urlMax, options: []) {
    
               let xml = SWXMLHash.parse(data)
               print(xml)
    
                metar1 = xml["response"]["data"]["METAR"]["raw_text"].element!.text!
               skycover = xml["response"]["data"]["METAR"]["sky_condition"][0].element?.attributes["sky_cover"]
             print(skycover)
    
                }
            }
        }
    

    Any help would be greatly appreciated!!

    question 
    opened by zdigitalpro 11
  • Trouble parsing this kind of XML

    Trouble parsing this kind of XML

    I've been using this framework for a while, and had no trouble with it. But now I need to parse this kind of response and I cannot understand/find a way to build a model class to parse this.

    Any help?

    <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="urn:xxxxxxxxxxxx">
       <SOAP-ENV:Body>
          <ns1:BuscarDebitosPendentesResponse xmlns:ns1="BuscarDebitosPendentes">
    
             <return xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:DebitosPendentes[19]">
                 <item xsi:type="tns:DebitosPendentes">
                     <Vencimento xsi:type="xsd:string">xxxxxxxxxxxx</Vencimento>
                     <ValorAtualizado xsi:type="xsd:decimal">xxxxxxxxxxxx</ValorAtualizado>
                     <Valor xsi:type="xsd:decimal">xxxxxxxxxxxx</Valor>
                     <Sequencia xsi:type="xsd:string">xxxxxxxxxxxx</Sequencia>
                     <NroBanco xsi:type="xsd:int">xxxxxxxxxxxx</NroBanco>
                     <BcoCobr xsi:type="xsd:int">xxxxxxxxxxxx</BcoCobr>
                     <Cobranca xsi:type="xsd:string">xxxxxxxxxxxx</Cobranca>
    
                     <Itens xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:ListaItens[1]">
                         <item xsi:type="tns:ListaItens">
                            <Descricao xsi:type="xsd:string">xxxxxxxxxxxx</Descricao>
                            <ValorItem xsi:type="xsd:decimal">xxxxxxxxxxxx</ValorItem>
    
                      </item>
                   </Itens>
                </item>
    
    question 
    opened by JFSene 9
  • Add automatic type conversion

    Add automatic type conversion

    This PR adds automatic type conversion, like this:

    let books: [Book] = try xml["root"]["books"]["book"].value()
    

    Inspired by discussion in https://github.com/drmohundro/SWXMLHash/issues/10

    opened by ncreated 9
  • Rename Framework

    Rename Framework

    https://forums.swift.org/t/frameworkname-is-not-a-member-type-of-frameworkname-errors-inside-swiftinterface/28962

    I have this issue when I create my XCFramework and the build tool generate my .swiftinterface files. With my dependency on SWXMLHash and a contained class of the same name, I run into the same issues found in the link above.

    The short of it is that the classes in your framework are listed in the .swiftinterface file and the framework name is prepended. So XMLIndexerDeserializable becomes SWXMLHash.XMLIndexerDeserializable and Swift looks in the SWXMLHash class for XMLIndexerDeserializable.

    The above link also contains a link to the issue to address, https://forums.swift.org/t/pitch-fully-qualified-name-syntax/28482, however, doesn't look like this will be available any time soon.

    cleanup 
    opened by fgarciaDev 8
  • Elements with namespaces have their element names

    Elements with namespaces have their element names "switched"

    Running the following on Ubuntu (via Docker):

    let podcastURL = URL(string: "http://feed.thisamericanlife.org/talpodcast")!
    let contents = try String(contentsOf: podcastURL, encoding: .utf8)
    let xml = SWXMLHash.parse(contents)
    
    print(contents)
    print("-----")
    print(xml.description)
    

    will switch an element name (around the :) when it has a namespace. For e.g., the string contents has

    <itunes:image href="http://www.thisamericanlife.org/sites/all/themes/thislife/images/logo-square-1400.jpg" />
    

    while the xml print out gives:

    <image:itunes href="http://www.thisamericanlife.org/sites/all/themes/thislife/images/logo-square-1400.jpg"></image:itunes>
    

    Note: this is only on Linux; the same code on macOS gives the correct element name.


    Platform: ubuntu:16.04 Xcode 9.2, Swift 4.0.3

    linux 
    opened by raheelahmad 4
  • Type conversion with nested nodes

    Type conversion with nested nodes

    I was wondering if SWXMLHash supports nested nodes of the same type.

    Given the following XML:

    <?xml version="1.0" encoding="UTF-8"?>
    <nodes>
        <node>
            <name>Dog</name>
            <node>
                <name>Labrador</name>
            </node>
        </node>
        <node>
            <name>Cat</name>
        </node>
        <node>
            <name>Mouse</name>
        </node>
    </nodex>
    

    And the code:

    let xml = SWXMLHash.parse(content!)
    
    struct Node: XMLIndexerDeserializable {
        let name: String
        let node: [Node]?
    
        static func deserialize(node: XMLIndexer) throws -> Node {
            return try Node(
                name: node["name"].value(),
                node: node["node"].value()
            )
        }
    
    }
    
    do {
        let nodes: [Node] = try xml["nodes"]["node"].value()
        print(nodes)
    } catch let error {
        print(error)
    }
    

    This results in the message "This node is invalid: \n". This is working with child nodes, that are not of type Node.

    bug 
    opened by olisim 6
Releases(7.0.1)
  • 7.0.1(May 7, 2022)

    Renaming XMLIndexerDeserializable protocol and Adding XMLValueDeserialization protocol

    I renamed XMLIndexerDeserializable to be XMLObjectDeserialization - its purpose is to deserialize object structures like classes or structs. So, multiple values. Then, there is a new protocol named XMLValueDeserialization - it serves the purpose of both XMLElementDeserializable and XMLAttributeDeserializable. It is only used for custom value deserialization. I hope this will clarify the usage a lot. It makes sense to me anyway!

    This should cover the use case at https://github.com/drmohundro/SWXMLHash/discussions/148.

    Usability of Errors

    Multiple times when helping people, I'd notice that the nodeIsInvalid error would get thrown, but the error message was just about impossible to determine what was wrong. The code had implemented CustomStringConvertible but everyone was using localizedDescription. Now, errors implement LocalizedError, too, so it is a lot more obvious to see the elements that had caused issues.

    Code Reorganization

    The code had all been lumped into two Swift files since the beginning. It was unwieldy then, but it has just gotten worse. So I gave in and broke them all out into their own files by type. Much better.

    Source code(tar.gz)
    Source code(zip)
  • 6.0.0(Sep 29, 2021)

    Released on 2021-09-28.

    • Rename main class from SWXMLHash to XMLHash so that the module name (i.e. SWXMLHash) doesn't conflict with the class name. There are a decent number of issues related to naming conflicts so hopefully this will avoid them. Fixes #242, see also links on PR #246 where other similar issues are documented.
    Source code(tar.gz)
    Source code(zip)
  • 5.0.0(Aug 29, 2019)

    Released on 2019-08-28.

    • Added official/full Swift 5.1 compatibility (via #216)
      • Note that API compatibility hasn't changed... it is a major version upgrade to support new language features.
    Source code(tar.gz)
    Source code(zip)
  • 4.9.0(Mar 29, 2019)

  • 4.8.0(Mar 6, 2019)

  • 4.7.6(Dec 11, 2018)

  • 4.7.5(Nov 26, 2018)

  • 4.7.4(Nov 26, 2018)

  • 4.7.2(Nov 26, 2018)

  • 4.7.1(Aug 4, 2018)

  • 4.7.0(May 7, 2018)

  • 4.6.0(Mar 10, 2018)

  • 4.5.1(Mar 8, 2018)

  • 4.5.0(Feb 21, 2018)

  • 4.4.1(Feb 21, 2018)

  • 4.4.0(Feb 4, 2018)

  • 4.3.6(Jan 21, 2018)

  • 4.3.5(Jan 15, 2018)

  • 4.2.5(Nov 10, 2017)

  • 4.2.4(Nov 9, 2017)

  • 4.2.3(Sep 28, 2017)

  • 4.2.2(Sep 22, 2017)

  • 4.2.1(Sep 22, 2017)

  • 4.2.0(Sep 22, 2017)

  • 4.1.1(Sep 22, 2017)

  • 4.1.0(Sep 22, 2017)

  • 4.0.0(Sep 22, 2017)

    Released on 2017-05-15.

    • Changed all enums to be camel cased (e.g. thisValue versus ThisValue) to match the Swift naming recommendations
    • Removed Sequence from XMLIndexer to avoid a conflict with the element property coming in Swift 4
      • As a result, for-in no longer works directly against XMLIndexer; however, for-in works just fine against the .all method
    • Changed .text to no longer be optional

    (via #133, #134 and #135)

    Source code(tar.gz)
    Source code(zip)
  • 3.1.0(Sep 24, 2017)

    Released on 2017-05-15.

    Added recursiveText property which pulls the text property from the currently indexed element and all child elements. (see PR #131)

    Source code(tar.gz)
    Source code(zip)
  • 3.0.5(Sep 24, 2017)

  • 3.0.4(Sep 24, 2017)

Owner
David Mohundro
Dad, Code, Tech, Games, comma-delimited lists.
David Mohundro
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 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
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
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
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
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
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 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
Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable

Elevate is a JSON parsing framework that leverages Swift to make parsing simple, reliable and composable. Elevate should no longer be used for

Nike Inc. 611 Oct 23, 2022