A sensible way to deal with XML & HTML for iOS & macOS

Overview

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 cumbersome to work with. NSXMLDocument, offers a more convenient DOM-style API, but is only supported on macOS.

Ono offers a sensible way to work with XML & HTML on Apple platforms in Objective-C and Swift

Whether your app needs to scrape a website, parse an RSS feed, or interface with a XML-RPC webservice, Ono will make your day a whole lot less terrible.

Ono (斧) means "axe", in homage to Nokogiri (鋸), which means "saw".

Features

  • Simple, modern API following standard Objective-C conventions, including extensive use of blocks and NSFastEnumeration
  • Extremely performant document parsing and traversal, powered by libxml2
  • Support for both XPath and CSS queries
  • Automatic conversion of date and number values
  • Correct, common-sense handling of XML namespaces for elements and attributes
  • Ability to load HTML and XML documents from either NSString or NSData
  • Full documentation
  • Comprehensive test suite

Installation

CocoaPods is the recommended method of installing Ono. Add the following line to your Podfile:

Podfile

pod 'Ono'

Usage

Swift

import Foundation
import Ono

guard let url = Bundle.main.url(forResource: "nutrition", withExtension: "xml"),
    let data = try? Data(contentsOf: url) else
{
    fatalError("Missing resource: nutrition.xml")
}

let document = try ONOXMLDocument(data: data)
document.rootElement.tag

for element in document.rootElement.children.first?.children ?? [] {
    let nutrient = element.tag
    let amount = element.numberValue!
    let unit = element.attributes["units"]!

    print("- \(amount)\(unit) \(nutrient)")
}

document.enumerateElements(withXPath: "//food/name") { (element, _, _) in
    print(element)
}

document.enumerateElements(withCSS: "food > serving[units]") { (element, _, _) in
    print(element)
}

Objective-C

#import "Ono.h"

NSData *data = ...;
NSError *error;

ONOXMLDocument *document = [ONOXMLDocument XMLDocumentWithData:data error:&error];
for (ONOXMLElement *element in document.rootElement.children) {
    NSLog(@"%@: %@", element.tag, element.attributes);
}

// Support for Namespaces
NSString *author = [[document.rootElement firstChildWithTag:@"creator" inNamespace:@"dc"] stringValue];

// Automatic Conversion for Number & Date Values
NSDate *date = [[document.rootElement firstChildWithTag:@"created_at"] dateValue]; // ISO 8601 Timestamp
NSInteger numberOfWords = [[[document.rootElement firstChildWithTag:@"word_count"] numberValue] integerValue];
BOOL isPublished = [[[document.rootElement firstChildWithTag:@"is_published"] numberValue] boolValue];

// Convenient Accessors for Attributes
NSString *unit = [document.rootElement firstChildWithTag:@"Length"][@"unit"];
NSDictionary *authorAttributes = [[document.rootElement firstChildWithTag:@"author"] attributes];

// Support for XPath & CSS Queries
[document enumerateElementsWithXPath:@"//Content" usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) {
    NSLog(@"%@", element);
}];

Demo

Build and run the example project in Xcode to see Ono in action, or check out the provided Swift Playground.

Requirements

Ono is compatible with iOS 5 and higher, as well as macOS 10.7 and higher. It requires the libxml2 library, which is included automatically when installed with CocoaPods, or added manually by adding "libxml2.dylib" to the target's "Link Binary With Libraries" build phase.

Contact

Mattt

License

Ono is available under the MIT license. See the LICENSE file for more info.

Comments
  • Fix XPathFromCSS bug(general) & test case compile error(xcode 7)

    Fix XPathFromCSS bug(general) & test case compile error(xcode 7)

    https://github.com/mattt/Ono/commit/002d2f2a80ad9ca1eded9f8ac894b2c69437dd39 Fixed a bug in XPathFromCSS that mistakenly matches the CSS string rather than token string, test case added for this bug.

    https://github.com/mattt/Ono/commit/94f30a52239bd578cdf01814140e8c9fb954549a Added explicit type cast to fix compilation error for test in Xcode 7 beta, where NSArray returns ObjectType element rather than id. This will not affect previous versions of Xcode. Please refer to 'Objective-C Language Features' in http://adcdownload.apple.com/Developer_Tools/Xcode_7_beta_4/Xcode_7_beta_4_Release_Notes.pdf

    opened by cezheng 6
  • Build error in XCode7.1

    Build error in XCode7.1

    Tests/ONOHTMLTests.m file

    - (void)testRootElementChildren {
        NSArray *children = [self.document.rootElement children];
        XCTAssertNotNil(children, @"children should not be nil");
        XCTAssertTrue([children count] == 2, @"root element has more than two children");
        XCTAssertEqualObjects([[children firstObject] tag], @"head", @"head not first child of html");
        XCTAssertEqualObjects([[children lastObject] tag], @"body", @"body not last child of html");
    }
    

    The last two line of that method occurs following error :

    "Multiple methods named 'tag' found with mismatched result, parameter type or attributes..."

    Is there any way to fix this? Please help me. Thanks.

    opened by alexchan1680 5
  • Native Swift support

    Native Swift support

    Hello Matt,

    Are you going to implement this library natively on Swift?

    I use this library in Swift project and there're several common Swift to Objective-C inconveniences, e.g.

    for object in element.childrenWithTag("td") {
       let child = object as! ONOXMLElement
       ...
    }
    

    NSFastEnumeration cannot be used in for-in directly

    - (id <NSFastEnumeration>)CSS:(NSString *)CSS;
    

    most methods return ImplicitlyUnwrappedOptional

    I can update the code base at least to use Objective-C Nullability.

    Please comment.

    opened by larryonoff 4
  • Carthage support

    Carthage support

    Hi,

    I’ve had problems with CocoaPods before, so liking the look of the more straight forward Carthage. I was wondering if I had to do anything special to get Ono working with Carthage.

    I get the error: Project "Ono.xcodeproj" has no shared schemes

    I will try building Ono manually and adding that to my Xcode project. Thanks.

    opened by RoyalIcing 4
  • Namespace info is not inherited on descendant nodes

    Namespace info is not inherited on descendant nodes

    a3211b847c3b4c480fb54e0c5b9ec2e3017e269d (context in #20) introduces a fix for a mysterious bug in libxml2 which prevents XML namespaces from being properly populated on xmlNodePtr objects, by using ->nsDef instead of ->ns.

    However, namespace definition (->nsDef) info is only available on the node in which the namespace definitions appear (as an XML attribute). This mean that XPath expressions cannot be used on descendant nodes that do not feature the namespace definitions. Put another way, using ->nsDef results in namespaces not being "inherited" by child nodes.

    Until the libxml2 bug is fixed (and Apple incorporates the fixed libxml2 into their developer tools distribution), one way to use an XPath expression from a descendant of a node on which an XML namespace has been defined is to walk up the ancestor hierarchy and harvest all of the namespace definitions.

    opened by erikprice 4
  • XPath queries do not work with namespaces and fixed encoding property

    XPath queries do not work with namespaces and fixed encoding property

    Hello (again!)

    I noticed that any XPath queries containing namespaces //ns1:tagname would fail with the error:

    XPath error : Undefined namespace prefix
    XPath error : Invalid expression
    

    I've added a test case for this issue and the Travis build file to ensure the tests on my fork pass :) https://travis-ci.org/rhodgkins/Ono/builds/26876794

    opened by rhodgkins 4
  • warnings when building with -Weverything

    warnings when building with -Weverything

    all of these individual warnings come up when compiling ONOXMLDocument.m with -Weverything

    -Wshadow -Wsign-conversion -Wfloat-conversion -Wconversion -Wnullable-to-nonnull-conversion -Wcast-qual

    opened by core-code 3
  • CSS selector does not work for some queries

    CSS selector does not work for some queries

    Thanks for the very helpful library! While using this with HTML, I found some CSS selector queries don't work. For example, when I tried to find elements under a specific element with a query like "#price .a-color-price", it generates the error below:

    XPath error : Invalid expression .//[@id = 'price']/descendant::[@id = 'a-col']*[contains(concat(' ',normalize-space(@class),' '),' a-color-price ')]

    Can you please fix this error if I'm not using CSS selector in a wrong way?

    opened by youngcha101 3
  • XCode 7.1

    XCode 7.1 "include of non-modular header inside module 'Ono.Ono'"

    I'm working on a pod called Spectra that uses Ono 1.2.2 to parse XML for a scene graph and some other things.

    I upgraded to XCode 7.1 yesterday and i haven't been able to resolve this build error at all. I've found a lot of people online asking about this, but for the most part, they're not resolving this issue for a pod their building. So many of the solutions i've found don't work for me.

    include of non-modular header inside framework module Ono.Ono
    

    I've found that by changing the following line in ono.h resolve the Ono build issues for me.

    // changed from this
    #import <Ono/ONOXMLDocument.h>
    
    // to this
    #import "ONOXMLDocument.h"
    
    opened by dcunited001 3
  • Crash inside -[ONOXMLElement attributes] for

    Crash inside -[ONOXMLElement attributes] for "description" key

    - (NSDictionary *)attributes {
        if (!_attributes) {
            NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionary];
            for (xmlAttrPtr attribute = self.xmlNode->properties; attribute != NULL; attribute = attribute->next) {
                NSString *key = @((const char *)attribute->name);
                // valueForAttribute is nil for 'description'
                [mutableAttributes setObject:[self valueForAttribute:key] forKey:key];
            }
    
            self.attributes = [NSDictionary dictionaryWithDictionary:mutableAttributes];
        }
    
        return _attributes;
    }
    

    Sample:

    <outline text="BBC Persian" description="&lt;div style=&quot;direction:rtl;text-align:right&quot;&gt;&#x627;&#x6CC;&#x646; &#x635;&#x641;&#x62D;&#x647; &#x62F;&#x6CC;&#x6AF;&#x631; &#x628;&#x647; &#x631;&#x648;&#x62E;.." htmlUrl="http://www.bbc.co.uk/blogs/persian/editors/" xmlUrl="http://www.bbc.co.uk/blogs/persian/editors/rss.xml" subscribe="false" content_type="text/xml" site_icon="http://www.bbc.co.uk/favicon.ico"/>
    
    opened by chrisballinger 3
  • Failing test cases related to issue #21 - firstChildWithTag: does not work with 'text'

    Failing test cases related to issue #21 - firstChildWithTag: does not work with 'text'

    See #21.

    Hopefully if the issue can be fixed these test cases can be used to help! I've created an issue and a pull request separately as this pull request does not fix the issue just shows it.

    opened by rhodgkins 3
  • Use of '@import' when C++ modules are disabled, consider using -fmodules and -fcxx-modules

    Use of '@import' when C++ modules are disabled, consider using -fmodules and -fcxx-modules

    When I use #import "Ono.h" in .mmfile ,it raises error below

    Use of '@import' when C++ modules are disabled, consider using -fmodules and -fcxx-modules
    

    but I change @import Foundation; with #import <Foundation/Foundation.h> in Ono.h ,it's fine

    opened by LinkRober 0
  • why textNode isn't child node

    why textNode isn't child node

    -(NSArray *)childrenAtIndexes:(NSIndexSet *)indexes {

    NSMutableArray *mutableChildren = [NSMutableArray array]; 
    xmlNodePtr cursor = self.xmlNode->children;
    NSUInteger idx = 0;
    while (cursor) {
        if ([indexes containsIndex:idx] && cursor->type == XML_ELEMENT_NODE) {
            [mutableChildren addObject:[self.document elementWithNode:cursor]];
        }
    
        cursor = cursor->next;
        idx++;
    }
    return [NSArray arrayWithArray:mutableChildren];
    

    }

    According to the above code I get child node must be element nodes, but also I think text node's child nodes. Namely: if ([indexes containsIndex:idx] && (cursor->type == XML_ELEMENT_NODE || cursor->type == XML_TEXT_NODE)) { [mutableChildren addObject:[self.document elementWithNode:cursor]]; }

    opened by ShengkunZhang 0
  • Feature Request: Not printing recursively for stringValue

    Feature Request: Not printing recursively for stringValue

    I would like to have a method similar to stringValue which doesn't recursively prints everything under a certain XPathQuery. Here is the full code + HTML and the produced output by Ono plus which output I'd like to have.

    My XPath Query: XPathQuery: //div[@class='thread']

    Ono code:

    document = [ONOXMLDocument HTMLDocumentWithData:file error:&error];
    
    [document enumerateElementsWithXPath:xPath usingBlock:^(ONOXMLElement *element, NSUInteger idx, BOOL *stop) {
        NSLog(@"%@", [element stringValue]);
    }];
    

    Which prints:

    FirstName LastName, SecondNameFirst SecondNameLast
    
    
                    FirstName LastName
                    Wednesday, December 24, 2014 at 6:57pm UTC+01 
    
    
            This is a dummy text
    
    
                    SecondNameFirst SecondNameLast
                    Wednesday, December 24, 2014 at 6:56pm UTC+01
    
    
            And a 2nd one just to show off
    
    
    Another, User
    
    
                    Another
                    Monday, April 27, 2015 at 10:54pm UTC+02
    
    
            Text: 2.1
    
    
                    User
                    Thursday, February 26, 2015 at 5:41pm UTC+01
    
    
            Text: 2.2
    
    
                    Another
                    Thursday, February 26, 2015 at 4:25pm UTC+01
    
    
            Text: 2.3
    

    I would prefer to have an output similar to hpple which is:

    FirstName LastName, SecondNameFirst SecondNameLast
    Another, User
    

    hpple code:

    tutorialsParser = [TFHpple hppleWithHTMLData:file];
    tutorialsNodes = [tutorialsParser searchWithXPathQuery:xPath];
    
    for (TFHppleElement *element in tutorialsNodes) {
        NSLog(@"%@", [[element firstChild] content].trim);
    }
    

    And I don't want to use hpple since it is too slow.

    Here is my input HTML file:

    <!DOCTYPE html>
    <html>
    <head><title/></head>
    <body>
        <div class="thread">FirstName LastName, SecondNameFirst SecondNameLast
            <div class="message">
                <div class="message_header">
                    <span class="user">FirstName LastName</span>
                    <span class="meta">Wednesday, December 24, 2014 at 6:57pm UTC+01 </span>
                </div>
            </div>
            <p>This is a dummy text</p>
            <div class="message">
                <div class="message_header">
                    <span class="user">SecondNameFirst SecondNameLast</span>
                    <span class="meta">Wednesday, December 24, 2014 at 6:56pm UTC+01</span>
                </div>
            </div>
            <p>And a 2nd one just to show off</p>
        </div>
        <div class="thread">Another, User
            <div class="message">
                <div class="message_header">
                    <span class="user">Another</span>
                    <span class="meta">Monday, April 27, 2015 at 10:54pm UTC+02</span>
                </div>
            </div>
            <p>Text: 2.1</p>
            <div class="message">
                <div class="message_header">
                    <span class="user">User</span>
                    <span class="meta">Thursday, February 26, 2015 at 5:41pm UTC+01</span>
                </div>
            </div>
            <p>Text: 2.2</p>
            <div class="message">
                <div class="message_header">
                    <span class="user">Another</span>
                    <span class="meta">Thursday, February 26, 2015 at 4:25pm UTC+01</span>
                </div>
            </div>
            <p>Text: 2.3</p>
        </div>
    </body>
    </html>
    
    opened by hashier 2
  • SOAP request fails

    SOAP request fails

    For the following XML document(SOAP service), XPath can't find the elements.

    <?xml version="1.0" encoding="UTF-8"?>
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <soapenv:Body>
            <ServiceResponse xmlns="http://sub.domain.com">
                <ServiceReturn>
                    <a>US</a>
                    <b>
                        <b>
                            <e>Herrod</e>
                            <f>[email protected]</f>
                            <g>Et Commodo LLC</g>
                            <i>07/14/2014</i>
                        </b>
                        <b>
                            <e>Armand</e>
                            <f>[email protected]</f>
                            <g>Vel Convallis In Consulting</g>
                            <i>04/18/2015</i>
                        </b>
                        <b>
                            <e>Bernard</e>
                            <f>[email protected]</f>
                            <g>Cursus Nunc Mauris PC</g>
                            <i>11/12/2015</i>
                        </b>
                        <b>
                            <e>Dante</e>
                            <f>[email protected]</f>
                            <g>Sit Amet Ltd</g>
                            <i>01/19/2016</i>
                        </b>
                    </b>
                    <c>0</c>
                    <k>12345678</k>
                </ServiceReturn>
            </ServiceResponse>
        </soapenv:Body>
    </soapenv:Envelope>
    

    Here are the result of some functions

    document.rootElement.firstChildWithTag("Body", inNamespace: "soapenv") //some
    document.rootElement.firstChildWithTag("//soapenv:Body/ServiceResponse") //nil
    document.rootElement.firstChildWithTag("//Body//b", inNamespace: "soapenv") //nil
    document.firstChildWithXPath("//ServiceResponse") //nil
    document.firstChildWithXPath("//ServiceReturn") //nil
    document.firstChildWithXPath("//*/ServiceReturn/b/b") //nil
    document.firstChildWithXPath("//ServiceReturn/b/b") //nil
    document.firstChildWithXPath("//b/b") //nil
    

    Another example

    <?xml version="1.0" encoding="UTF-8"?>
    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
       <S:Body>
          <ns2:OperationHistoryData xmlns:ns2="http://www.domain.org/operation" xmlns:ns3="http://www.domain.org/info" xmlns:ns5="http://www.domain.org/data">
             <ns2:historyRecord>
                <ns2:DestinationAddress>
                   <ns2:Index>190000</ns2:Index>
                   <ns2:Description>Info 1</ns2:Description>
                </ns2:DestinationAddress>
             </ns2:historyRecord>
          </ns2:OperationHistoryData>
       </S:Body>
    </S:Envelope>
    

    Whenever there is nested namespaces, ono can't parse it. I don't know maybe it is limitation of libxml. Only workaround I found is to strip name spaces and other attributes.

    opened by tosbaha 5
Owner
Mattt
Mattt
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 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
📄 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 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
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
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
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
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
Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Convert text with HTML tags, links, hashtags, mentions into NSAttributedString. Make them clickable with UILabel drop-in replacement.

Pavel Sharanda 1.1k Dec 26, 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
An Objective-C framework for your everyday HTML needs.

HTMLKit An Objective-C framework for your everyday HTML needs. Quick Overview Installation Parsing The DOM CSS3 Selectors Quick Overview HTMLKit is a

Iskandar Abudiab 229 Dec 12, 2022
Mathias Köhnke 1.1k Dec 16, 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
SensibleStyling - Helpers for sensible SwiftUI styling

SensibleStyling Helpers for sensible SwiftUI styling. License See the LICENSE fi

apparata 5 Dec 18, 2022