Git Product home page Git Product logo

swxmlhash's Introduction

SWXMLHash

Swift Platforms Swift Package Manager CocoaPods Compatible Carthage Compatible

SWXMLHash is a relatively simple way to parse XML in Swift. If you're familiar with XMLParser (formerly NSXMLParser), this library is a 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 Swift Package Manager, CocoaPods, Carthage, or manually.

Swift Package Manager

The Swift Package Manager is a tool built by Apple as part of the Swift project for integrating libraries and frameworks into your Swift apps.

To add SWXMLHash as a dependency, update the dependencies in your Package.swift to include a reference like so:

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

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

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', '~> 7.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" ~> 7.0

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 all of the relevant swift files into your project.

If you're using a workspace, though, you can include the entire SWXMLHash.xcodeproj.

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 = XMLHash.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 = XMLHash.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 = XMLHash.config {
              config in
              config.shouldProcessLazily = true
          }.parse(xmlToParse)

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

let xml = XMLHash.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 "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 Do-Catch with Errors:

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.

XML Deserialization Into Objects

Even more often, you'll want to deserialize an XML tree into an array of custom types. This is where XMLObjectDeserialization comes into play.

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 XMLObjectDeserialization:

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()
        )
    }
}

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 XMLObjectDeserialization 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.

Custom Value Conversion

Value deserialization is where a specific string value needs to be deserialized into a custom type. So, date is a good example here - you'd rather deal with date types than doing string parsing, right? That's what the XMLValueDeserialization attribute is for.

Given:

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

With the following implementation for Date value deserialization:

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 {
        // empty validate... only necessary for custom validation logic after parsing
    }

    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)
    }
}

The below will return a date value:

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

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 XMLObjectDeserialization (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 #245.

I'm seeing an ""'XMLElement' is ambiguous" Error

This is related to #256 - XMLElement has actually been renamed multiple times to attempt to avoid conflicts, but the easiest approach is to just scope it via XMLHash.XMLElement.

Will SWXMLHash work in a web context (e.g. Vapor)?

See #264 where this is discussed. The only change needed is to add the following import logic:

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

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.

swxmlhash's People

Contributors

ainopara avatar arcikbtw avatar christiansteffens avatar compnerd avatar dotcypress avatar drmohundro avatar funkenstrahlen avatar gca3020 avatar gitter-badger avatar gottagetswifty avatar hslightnin avatar jpsim avatar k-be avatar keith avatar klaaspieter avatar korzonek avatar limsangjin12 avatar martinpucik avatar mikepulsiferdol avatar mshibanami avatar ncreated avatar norio-nomura avatar otaviolima avatar rahul0x24 avatar rivera-ernesto avatar st3fan avatar thomas-em avatar tomassliz avatar woolie avatar yas375 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swxmlhash's Issues

Parsing XML with namespaces

Currently, there is no way to set the XMLParser shouldProcessNamespaces to true. So when accessing an element, the complete tagname with namespace needs to be passed. e.g xml["f:animal"] instead of just xml["animal"]

In the example in the code, it would be nice if instead of this

xml["root"]["h:table"]["h:tr"]["h:td"][0].element!.text!

we will have this

xml["root"]["table"]["tr"]["td"][0].element!.text!

Possible problem when using SWXMLHash inside a framework

Please have a look at https://github.com/tkrajacic/XMLTestWithFramework
It is a minimal crashing example.

When the application is finished processing the XML file it crashes when trying to over-release some CFString. This only happens when using a NSNumberFormatter to parse a String out of the XML.
When commenting out the String parsing, everything works.

I am not sure if I am making a mistake using SWXMLHash here or if it is a bug inside SWXMLHash.

Any help would be appreciated!

Type conversion fails with lazily loaded XML

When using XML parsed with the SWXMLHash.lazy() method, the type conversion to an array fails with a XMLDeserializationError.NodeIsInvalid

The problem is fixed when changing to SWXMLHash.parse()

I'm unable to use the framework in the playground

The framework is working fine in my project, but I'm unable to import the framework in the playground.

I keep having this output in the console:
Playground execution failed: bart-Sample-playground.playground:4:8: error: no such module 'SWXMLHash'

import SWXMLHash
^

  • I tried to delete the content of the derived data folder.
  • I also tried to clean and build.

How can I use the framework inside a playground ?

Interleaving XML elements support

Hi David,

Thanks for the library, I like the API of this library but have one feature request (or bug report?) on it.

In some XML documents, elements with the same name can appear in any order instead of appear continuously. For example, an XHTML document:

<html>
  <body>
    <p>one</p>
    <div>two</div>
    <p>three</p>
    <div>four</div>
  </body>
</html>

As you can see, tag p and tag div can interleave. And to parse and render this XHTML correctly, all the elements order in this document should be kept. So one-two-three-four should be shown in the browser instead of one-three-two-four. However, this seems not possible in current implementation.

Right now, in current implementation, all child elements are stored in an ordered dictionary, whose key is the element name and value is an array of elements with the same element name. When adding an element, the new element will be appended to the dictionary entry with the element name, and when retrieving all children, the entire dictionary will be iterated entry by entry.

Is there any plan to support the XML document with interleaving pattern like above? The current implementation seems to me incorrect when handling such interleaving pattern by giving me incorrect elements order because it will groups elements by their names first.

To support this interleaving pattern, we could simply use an array to replace the ordered dictionary, and this will not only make the implementation correct (XML document is essentially a linear document I think) but also much simpler. The only down side is the subscript access by element name will be slower but that may be acceptable. What do you think?

XML with mixed content isn't currently supported

I discovered that XML that has mixed content (i.e. both child elements and text) isn't currently supported.

For example:

<root>
  <header>
    header mixed content
    <title>Test Title Header</title>
  </header>
</root>

The following is a failing spec (it actually returns nil at the moment):

it("should be able to handle mixed content") {
  expect(xml["root"]["header"].element?.text).to(equal("header mixed content"))
}

Carthage installation issue

Hi,

When I tried installing SWXMLHash using Carthage, I got the following error

The following build commands failed:
Check dependencies
(1 failure)
Code Sign error: No code signing identities found: No valid signing identities (i.e. certificate and private key pair) matching the team ID “(null)” were found.
CodeSign error: code signing is required for product type 'Framework' in SDK 'iOS 8.4'

Please help.

Outputting XML and writing XML?

Beautifully written library. Congratulations!
I noticed that I can assign in new value to the text of an element, because it's a var string optional, so that works great. I hope I'm doing that the way that you intended.

let filePath = NSBundle(forClass: self.classForCoder).pathForResource("cds", ofType: "xml")
var contents = File.open(filePath!, utf8: NSUTF8StringEncoding)!
let xml = SWXMLHash.parse(contents)
println(xml["CATALOG"]["CD"][0]["TITLE"].element?.text!)
// is this how I should set the new text?
xml["CATALOG"]["CD"][0]["TITLE"].element?.text = "New Text"
//Output XML here somehow. 

I'm wondering if I can output the new XML so I can save it to document?

I understand the objects that all and children return. I was about to write something that would output the new XML with the new values. I wasn't sure if there was a easy way to get that without having to write something special that outputs XML.

For this example I am using this XML as an example:
http://www.w3schools.com/xml/cd_catalog.xml

Printing 'nil' for every value I try to retrieve

Hello,

I'm trying to get SWXMLHash working with my project. I have the URL of an XML file. When I try to retrieve the 'text' of certain elements, as well as attributes, I keep getting 'nil'. I must be doing something wrong, but I don't know what.

I've installed SWXMLHash via CocoaPods.

A sample of what the XML file looks like is,

<coord2 count="9">
  <markers>
    <marker>
      <lat>36.97734</lat>
      <lng>-122.05444</lng>
      <timestamp>1445016544</timestamp>
      <route>LOOP</route>
      <id>7855</id>
      <predictions>
      ,8,9,,10,11,,,,14,,,15,,15,,17,1,,0,,,3,,,4,,,6,,,,,,,,,,,,,,
      </predictions>
      <update_seconds>10</update_seconds>
      <index>0</index>
    </marker>
    <marker>
      <lat>36.97772667</lat>
      <lng>-122.053615</lng>
      <timestamp>1445016539</timestamp>
      <route>LOOP</route>
      <id>7860</id>
      <predictions>
      9,,,8,,,7,6,5,,4,3,,2,,1,,,0,,15,14,,13,12,,11,10,,,,,,,,,,,,,,,
      </predictions>
      <update_seconds>10</update_seconds>
      <index>0</index>
    </marker>
  </markers>
  <curr_time>1445016544</curr_time>
</coord2>

The following is what my code looks like,

import UIKit
import SWXMLHash

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let xml = SWXMLHash.config {
            config in
            config.shouldProcessNamespaces = true
            }.parse("http://url.to.xmlfile.xml")

        print(xml["coord2"].element?.attributes["count"])
        print(xml["coord2"]["markers"]["marker"][0]["lat"].element?.text)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

I'm expecting my program to output:
9
36.97734

but I'm getting:
nil
nil

I am running Xcode v 7.0.1 (Swift 2.0 I think).

Thank you for your help!

Deserializing a single element into an array

Hi there, I'm wondering if its possible to deserialize a single XML element into an array.

For example with the given xml
<distances>1,2,3,4,5,6,7</distances>

Is it possible to deserialize into [Int] ?

Thanks!

Add Travis CI integration

I've started this by adding a .travis.yml file, but it isn't building yet... still working out Carthage and pulling in Quick/Nimble so that the tests can be run.

Allow easy enumeration of child elements.

I thought that .all would allow for easy enumeration of child elements (such as when the XML is dynamic), but it doesn't appear to work.

The API I have in mind is something like:

func enumerate(xml: XMLIndexer) {
    for elem in xml.all {
        enumerate(elem)
    }
}

enumerate(xml)

add .all to documentation

Took me a few reads to figure out that xml["root"]["catalog"]["book"].all would retrieve "all books under catalog in an array". Maybe I'm dumb.

This is a great tool, thank you.

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!!

Parsing question

First off, many thanks for this library and great work

I managed to get the rss xml parsed and it looks like this

<rss version="2.0">
     <channel>
        <title>
       </title>
        <title>
       </title>
         .......
     </channel>
</rss>

So I'm trying to extract the title as such but its not returning anything.

        for elem in xml["rss version=\"2.0\""]["channel"] {

            print(elem["title"].element!.text!)
        }

Does it return NSArray or NSDictionary?

    var xml = NSData(contentsOfFile: NSString(format: "%@/test.xml", NSBundle.mainBundle().resourcePath!));
    let dom = SWXMLHash.parse(xml)

    let elems = dom["root"]
    for e in elems {

    }

I got the following error message:

Type 'XMLIndexer' does not conform to protocol 'SequenceType'

How can it be supported? Thanks

Add support for automatic deserialization of Bool types

The automatic type conversion in #68 lacks support for Bool types. The W3C specification defines a boolean as follows:

3.2.2.1 Lexical representation

An instance of a datatype that is defined as ·boolean· can have the following legal literals {true, false, 1, 0}.

SWXMLHash should support automatic decoding of at least these four values into Swift Bool primitives.

SWXMLHash.parse missing some data

When i put message
responseData = <div>
<div>
<div class="h">Header 1</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
<div>
<div class="h">Header 2</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
<div>
<div class="h">Header 3</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
<div>
<div class="h">Header 4</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
<div>
<div class="h">Header 5</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
</div>
"
let xml = SWXMLHash.parse(responseData )
and println(xml)
"
<div>
<div>
<div class="h">Header 1</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
<div>
<div class="h">Header 2</div>
<div class="b">Body</div>
<div class="f">Footer</div>
</div>
"

It just has only 2 childs.
Please help

Incomplete XML

Hello,

I have the bellow code:

let xml = SWXMLHash.parse(data!)

let servResponse = xml["response"]["sucesso"].element?.text == "true" ? true : false

if servResponse == true {

      let resData = xml["response"]

       for obj in resData["XMLGridData"] {

        ...

       }

}

The XML that I'm looping is huge and contains 200 tags of "XMLGridData"..

But when I do the SWXMLHash.parse(data!) from the Alamofire it only iterates with 10 tags of "XMLGridData".

See the Xml limited
<response> <sucesso>true</sucesso> <retorno>Consulta Realizada Com Sucesso!</retorno> <XMLGridData> <Bike_Sq>2</Bike_Sq> <Bk_Bike_ID>1</Bk_Bike_ID> <PT_agency_id>1</PT_agency_id> <PT_agency_name>Bikesampa</PT_agency_name> <Bk_PL_ID>2</Bk_PL_ID> <Bk_Total>12</Bk_Total> <Bk_Available>1</Bk_Available> <Bk_Last_UPDATE>2016-05-15T19:00:00</Bk_Last_UPDATE> <Bk_Active>1</Bk_Active> <PL_LT>-2.358731500000000e+001</PL_LT> <PL_LG>-4.664797400000000e+001</PL_LG> <PL_Desc>BikeSampa -Instituto Biologico</PL_Desc> </XMLGridData> <XMLGridData> <Bike_Sq>3</Bike_Sq> <Bk_Bike_ID>2</Bk_Bike_ID> <PT_agency_id>1</PT_agency_id> <PT_agency_name>Bikesampa</PT_agency_name> <Bk_PL_ID>3</Bk_PL_ID> <Bk_Total>14</Bk_Total> <Bk_Available>0</Bk_Available> <Bk_Last_UPDATE>2016-05-15T19:00:00</Bk_Last_UPDATE> <Bk_Active>1</Bk_Active> <PL_LT>-2.359222700000000e+001</PL_LT> <PL_LG>-4.664684400000000e+001</PL_LG> <PL_Desc>BikeSampa -Cinemateca</PL_Desc> </XMLGridData> <XMLGridData> <Bike_Sq>4</Bike_Sq> <Bk_Bike_ID>3</Bk_Bike_ID> <PT_agency_id>1</PT_agency_id> <PT_agency_name>Bikesampa</PT_agency_name> <Bk_PL_ID>4</Bk_PL_ID> <Bk_Total>12</Bk_Total> <Bk_Available>1</Bk_Available> <Bk_Last_UPDATE>2016-05-15T19:00:00</Bk_Last_UPDATE> <Bk_Active>1</Bk_Active> <PL_LT>-2.359309900000000e+001</PL_LT> <PL_LG>-4.664239800000000e+001</PL_LG> <PL_Desc>BikeSampa -Capitao Macedo</PL_Desc> </XMLGridData> <XMLGridData> <Bike_Sq>5</Bike_Sq> <Bk_Bike_ID>4</Bk_Bike_ID> <PT_agency_id>1</PT_agency_id> <PT_agency_name>Bikesampa</PT_agency_name> <Bk_PL_ID>5</Bk_PL_ID> <Bk_Total>12</Bk_Total> <Bk_Available>2</Bk_Available> <Bk_Last_UPDATE>2016-05-15T19:00:00</Bk_Last_UPDATE> <Bk_Active>1</Bk_Active> <PL_LT>-2.358947200000000e+001</PL_LT> <PL_LG>-4.663995300000000e+001</PL_LG> <PL_Desc>BikeSampa -Belas Artes</PL_Desc> </XMLGridData> ..... </response>

I have checked the data from Alamofire and it is complete.

Could you please help me with this?

Compile error - CLI Project

Initial Cococapod install worked and I was able to play around in playground.

I then went to implement the code outside of the playground by adding "import SWXMLHash". I know get the following compile error

"<unknown>:0: error: could not build Objective-C module 'SWXMLHash'" 

which isn't very helpful

My playground also stopped working with the following error

"Playground execution failed: could not build Objective-C module 'SWXMLHash'A fatal parse error has occurred.  LLDB may become unstable; please restart your debug session as soon as possible."

I suspect it is an Xcode build / config problem or pod build problem since it initial worked in the playground.

I tried replicating the initial setup that worked with the playground, but haven't been able to.

I am lost at how to track the issue down? Any tips, tricks or suggestions?

tvOS target?

Hi

Would it be possible to get a tvOS target? (I've forked a copy and added the new target for tvOS, and it seems to compiles fine, and even builds a framework but I couldn't get Carthage to see the tvOS scheme... I'll be happy to issue a PR for my (utterly trivial) changes but might be faster for you to just add the target).

best,
David

A weird parser error.

Hello, Thank you for your framework.

I have a Custom UIView Template written in XML. And there's seems to be an escaping issue and I have no idea how to fix it. My XML seems right.

Here's the XML Template:

<?xml version="1.0" encoding="UTF-8"?>
<niotemplate version="1.0">
    <section>
        <textfield id="title" tinted="true" size="15">
            <placeholder>
                <en>Title</en>
            </placeholder>
        </textfield>
        <constraint>V:|-10-[title]-10-|</constraint>
        <constraint>H:|-15-[title]-15-|</constraint>
    </section>
    <section>
        <textview id="content" parser="markdown" editable="true" size="14" auto-height="true"/>
        <constraint>H:|-15-[content]-15-|</constraint>
        <constraint>V:|-10-[content]-10-|</constraint>
    </section>
</niotemplate>

There's an error when parsing <constraint>H:|-15-[title]-15-|</constraint>. SWXMLHash generate extra hidden characters which I cannot see them in console. When I try to use the AutoLayout Constraint String, It crash.

Here's the console log:

H:|-15-[title]-15-|

                   ^'

My XML seems right. And I have no idea how to fix it. Is it a problem from my XML doc? Or is it a parsing error which can be solved by SWXMLHash? I need help.

Thank you again!

Cannot compile with Xcode 6.1.1

When I add SWXMLHash.swift to my project I get the following errors:

Errors

Am I doing something wrong? I'm using OSX 10.10 and targeting My Mac.

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 }, }>

tvOS target has no sources to compile :\

Hi David,

Just got around to adding the tvOS framework to my project (spent the last week writing an importer to capture data which was mostly under OS X) and noticed I was getting an error in the editor when I tried to write

let xml = SWXMLHash.parse(data!)

that there was no parse method on the object when using the framework in the tvOS branch. I'd been using it all week in iOS and OS X so that was a little confusing :| ...turns out the swift file isn't in the build list for tvOS:

screen shot 2015-11-20 at 3 43 45 pm

(just did a fresh pull, just to check, and it's the same on the master branch)

Ensure everything works in XCode 6 beta 4.

Now that XCode 6 beta 4 is out, everything needs to be reviewed.

  • The specs currently are not running because Quick hasn't had a chance to update to XCode 6 beta 4... it will need to update (which I think mainly includes adding public accessors) before it will be usable again.
  • Public accessors will need to be added to everything that will be usable outside of the library.

Issue when return string

Hi,
when I try to print some text value , it return optional(" ----text ")
for example if I have this xml:
<root>
...
<catalog>
<book>Bob</book>
<book>John</book>
<book>Mark</book>
</catalog>
...
</root>

when I try to print xml["root"]["catalog"]["book"][1]["author"].element?.text

the output is optional( " John ") instade of "John".

Any idea?

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.

Add Carthage Support

I'm currently working on a fork to add this, but if you want to add it before then that would be killer.

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.

iOS9?

i see that it's targetting iOS8. will this work on iOS9 as well?

I'm running swift 2 and ios9, just curious if this is will work or not?

Swift 1.2 merge

When is the Xcode 6.3 branch going to be merged into master and pushed to Cocoapods?

I'm waiting on this right now! :) Thanks.

watchOS Support?

[!] The platform of the target Watch Extension (watchOS 2.0) is not compatible with SWXMLHash (2.0.0), which does not support watchos.

and there is my podfile

use_frameworks!
inhibit_all_warnings!

target 'MyMainAppTarget' do
    platform :ios, '8.0'

    ...
end

target 'MyWatchExtensionTarget' do
    platform :watchos, '2.0'

    pod 'SWXMLHash'
end

API Review before 1.0 release

The current release is 0.6.0. I want to ensure the API is solid before calling this 1.0 (as I'd like to use semantic versioning). I'd welcome thoughts or suggestions from anyone who makes use of this library, particularly if some method or property names aren't particularly clear.

Some general thoughts:

  • Documentation currently refers to a "current level" of indexing (i.e. xml["root"]["header"] is indexing down to "root" and "header"), but this seems vague to me. I'm like to re-think how to describe this.
    • As examples, the all and children properties behave differently depending on where the code has indexed to.
  • Both all and children might be too vague in their names.

I may consider renaming some methods and properties to help with clarity, but again, I'd welcome input, too.

Need help parsing an array node

Hi,

I have an xml which looks like the below:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <GetAssociatedProjectsIDsResponse>
            <GetAssociatedProjectsIDsResult xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
                <a:int>2947</a:int>
                <a:int>3847</a:int>
                <a:int>2675</a:int>
                <a:int>9875</a:int>
            </GetAssociatedProjectsIDsResult>
        </GetAssociatedProjectsIDsResponse>
    </s:Body>
</s:Envelope>

Is there a way to iterate within all 'int' elements inside 'GetAssociatedProjectsIDsResult'?

EXC_BAD_ACCESS

let parser = NSXMLParser(data: data)
parser.shouldProcessNamespaces = options.shouldProcessNamespaces
parser.delegate = self
parser.parse() ----> Line 227 - EXC_BAD_ACCESS (code=2, address=0x10f388000)

Occurs when running in iOS simulator

Help in parsing this xml format

Could you please help me in parsing this format.
I am not able to correctly parse this kind of format: I want to get the 'text' attributes value of all prop given 'child' index value.
For ex: get all prop's 'text' value given 'child' index 1
root
child index="1" name="child name 1"
prop index="1" text="prop name1" /
prop index="2" text="prop name2" /
/child
child index="2" name="child name 2"
prop index="1" text="prop name1" /
prop index="2" text="prop name2" /
/child
/root

watchOS build doesn't actually include the source file

I was banging my head against this for a couple of hours, getting an error "Module SWXMLHash has no member named ‘parse" in my WatchKit extension code. It turned out that the SWXMLHash WatchOS target doesn't actually include the single SWXMLHash.swift source file in its WatchOS target, so indeed, the framework really didn't have a member named parse.

I fixed this locally by selecting the project to see build settings, selecting the WatchOS target, then Build Phases > Compile Sources, then dragged SWXMLHash.swift into the empty list of source files.

I'd submit a pull request to fix this, but given it's project settings, it's probably easier for all concerned for you (the author) to make the change directly, though I can submit a PR if you prefer.

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.