├── Example ├── Ono Tests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── Ono Tests-Info.plist │ ├── atom.xml │ ├── ONOXMLTests.m │ └── ONOAtomTests.m ├── Prefix.pch ├── Ono Example.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj ├── main.m ├── Ono_Example.1 └── nutrition.xml ├── Ono.xcworkspace └── contents.xcworkspacedata ├── LICENSE ├── Ono ├── Ono.h ├── ONOXMLDocument.h └── ONOXMLDocument.m └── README.md /Example/Ono Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /Example/Ono Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Ono.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 17 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Example/Ono Tests/Ono Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.mattt.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Ono Tests/atom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example Feed 4 | A subtitle. 5 | 6 | 7 | urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6 8 | 2003-12-13T18:30:02Z 9 | 10 | Atom-Powered Robots Run Amok 11 | 12 | 13 | 14 | urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a 15 | 2003-12-13T18:30:02Z 16 | Some text. 17 | 18 | John Doe 19 | johndoe@example.com 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Ono/Ono.h: -------------------------------------------------------------------------------- 1 | // Ono.h 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #ifndef __ONO__ 24 | #import "ONOXMLDocument.h" 25 | 26 | #define __ONO__ 27 | #endif 28 | -------------------------------------------------------------------------------- /Example/Ono Tests/ONOXMLTests.m: -------------------------------------------------------------------------------- 1 | // ONOXMLTests.m 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @interface ONOXMLTests : XCTestCase 26 | 27 | @end 28 | 29 | @implementation ONOXMLTests 30 | 31 | - (void)setUp { 32 | [super setUp]; 33 | } 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ono (斧) 2 | **A sensible way to deal with XML & HTML for iOS & Mac OS X** 3 | 4 | > Ono (斧) means "axe", in homage to [Nokogiri](http://nokogiri.org) (鋸), which means "saw". 5 | 6 | _Whoa. I didn't expect an XML parsing library to garner so much interest so quickly. I'll work to have something production-worthy (CocoaPod, Documentation, Tests) by the end of the week. — Mattt_ 7 | 8 | ## Usage 9 | 10 | ```objective-c 11 | #import "Ono.h" 12 | 13 | NSData *data = ...; 14 | NSError *error; 15 | 16 | ONOXMLDocument *document = [ONOXMLDocument XMLDocumentWithData:data error:&error]; 17 | for (ONOXMLElement *element in document.rootElement.children) { 18 | NSLog(@"%@: %@", element.tag, element.attributes); 19 | } 20 | 21 | // Automatic Conversion for Number & Date Values 22 | NSDate *date = [[document firstChildWithTag:@"CreatedAt"] dateValue]; // RFC 822 Timestamp 23 | NSInteger numberOfBytes = [[document firstChildWithTag:@"ContentSize"] numberValue] integerValue]; 24 | BOOL isPublic = [[document firstChildWithTag:@"Public"] numberValue] boolValue]; 25 | 26 | // Support for XPath & CSS Queries 27 | [document enumerateElementsWithXPath:@"//Content" block:^(ONOXMLElement *element) { 28 | NSLog(@"%@", element); 29 | }]; 30 | ``` 31 | 32 | ### Contact 33 | 34 | [Mattt Thompson](http://github.com/mattt) 35 | [@mattt](https://twitter.com/mattt) 36 | 37 | ## License 38 | 39 | Ono is available under the MIT license. See the LICENSE file for more info. 40 | -------------------------------------------------------------------------------- /Example/main.m: -------------------------------------------------------------------------------- 1 | // main.m 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | #import "Ono.h" 26 | 27 | int main(int argc, const char * argv[]) { 28 | @autoreleasepool { 29 | NSError *error = nil; 30 | NSString *XMLFilePath = [[@(__FILE__) stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"nutrition.xml"]; 31 | ONOXMLDocument *document = [ONOXMLDocument XMLDocumentWithData:[NSData dataWithContentsOfFile:XMLFilePath] error:&error]; 32 | if (error) { 33 | NSLog(@"[Error] %@", error); 34 | return 0; 35 | } 36 | 37 | NSLog(@"Root Element: %@", document.rootElement.tag); 38 | 39 | NSLog(@"\n"); 40 | NSLog(@"Daily Values:"); 41 | for (ONOXMLElement *dailyValueElement in [[document.rootElement firstChildWithTag:@"daily-values"] children]) { 42 | NSString *nutrient = dailyValueElement.tag; 43 | NSNumber *amount = [dailyValueElement numberValue]; 44 | NSString *unit = dailyValueElement[@"units"]; 45 | NSLog(@"- %@%@ %@ ", amount, unit, nutrient); 46 | } 47 | 48 | NSLog(@"\n"); 49 | NSString *XPath = @"//food/name"; 50 | NSLog(@"XPath Search: %@", XPath); 51 | [document enumerateElementsWithXPath:XPath block:^(ONOXMLElement *element) { 52 | NSLog(@"%@", element); 53 | }]; 54 | 55 | NSLog(@"\n"); 56 | NSString *CSS = @"food > serving[units]"; 57 | NSLog(@"CSS Search: %@", CSS); 58 | [document enumerateElementsWithCSS:CSS block:^(ONOXMLElement *element) { 59 | NSLog(@"%@", element); 60 | }]; 61 | } 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /Example/Ono_Example.1: -------------------------------------------------------------------------------- 1 | .\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. 2 | .\"See Also: 3 | .\"man mdoc.samples for a complete listing of options 4 | .\"man mdoc for the short list of editing options 5 | .\"/usr/share/misc/mdoc.template 6 | .Dd 2/27/14 \" DATE 7 | .Dt Ono Example 1 \" Program name and manual section number 8 | .Os Darwin 9 | .Sh NAME \" Section Header - required - don't modify 10 | .Nm Ono Example, 11 | .\" The following lines are read in generating the apropos(man -k) database. Use only key 12 | .\" words here as the database is built based on the words here and in the .ND line. 13 | .Nm Other_name_for_same_program(), 14 | .Nm Yet another name for the same program. 15 | .\" Use .Nm macro to designate other names for the documented program. 16 | .Nd This line parsed for whatis database. 17 | .Sh SYNOPSIS \" Section Header - required - don't modify 18 | .Nm 19 | .Op Fl abcd \" [-abcd] 20 | .Op Fl a Ar path \" [-a path] 21 | .Op Ar file \" [file] 22 | .Op Ar \" [file ...] 23 | .Ar arg0 \" Underlined argument - use .Ar anywhere to underline 24 | arg2 ... \" Arguments 25 | .Sh DESCRIPTION \" Section Header - required - don't modify 26 | Use the .Nm macro to refer to your program throughout the man page like such: 27 | .Nm 28 | Underlining is accomplished with the .Ar macro like this: 29 | .Ar underlined text . 30 | .Pp \" Inserts a space 31 | A list of items with descriptions: 32 | .Bl -tag -width -indent \" Begins a tagged list 33 | .It item a \" Each item preceded by .It macro 34 | Description of item a 35 | .It item b 36 | Description of item b 37 | .El \" Ends the list 38 | .Pp 39 | A list of flags and their descriptions: 40 | .Bl -tag -width -indent \" Differs from above in tag removed 41 | .It Fl a \"-a flag as a list item 42 | Description of -a flag 43 | .It Fl b 44 | Description of -b flag 45 | .El \" Ends the list 46 | .Pp 47 | .\" .Sh ENVIRONMENT \" May not be needed 48 | .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 49 | .\" .It Ev ENV_VAR_1 50 | .\" Description of ENV_VAR_1 51 | .\" .It Ev ENV_VAR_2 52 | .\" Description of ENV_VAR_2 53 | .\" .El 54 | .Sh FILES \" File used or created by the topic of the man page 55 | .Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact 56 | .It Pa /usr/share/file_name 57 | FILE_1 description 58 | .It Pa /Users/joeuser/Library/really_long_file_name 59 | FILE_2 description 60 | .El \" Ends the list 61 | .\" .Sh DIAGNOSTICS \" May not be needed 62 | .\" .Bl -diag 63 | .\" .It Diagnostic Tag 64 | .\" Diagnostic informtion here. 65 | .\" .It Diagnostic Tag 66 | .\" Diagnostic informtion here. 67 | .\" .El 68 | .Sh SEE ALSO 69 | .\" List links in ascending order by section, alphabetically within a section. 70 | .\" Please do not reference files that do not exist without filing a bug report 71 | .Xr a 1 , 72 | .Xr b 1 , 73 | .Xr c 1 , 74 | .Xr a 2 , 75 | .Xr b 2 , 76 | .Xr a 3 , 77 | .Xr b 3 78 | .\" .Sh BUGS \" Document known, unremedied bugs 79 | .\" .Sh HISTORY \" Document history if command behaves in a unique manner -------------------------------------------------------------------------------- /Example/Ono Tests/ONOAtomTests.m: -------------------------------------------------------------------------------- 1 | // ONOXMLTests.m 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | #import "Ono.h" 26 | 27 | @interface ONOAtomTests : XCTestCase 28 | @property (nonatomic, strong) ONOXMLDocument *document; 29 | @end 30 | 31 | @implementation ONOAtomTests 32 | 33 | - (void)setUp { 34 | [super setUp]; 35 | 36 | NSError *error = nil; 37 | NSString *filePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"atom" ofType:@"xml"]; 38 | self.document = [ONOXMLDocument XMLDocumentWithData:[NSData dataWithContentsOfFile:filePath] error:&error]; 39 | 40 | XCTAssertNotNil(self.document, @"Document should not be nil"); 41 | XCTAssertNil(error, @"Error should not be generated"); 42 | } 43 | 44 | #pragma mark - 45 | 46 | - (void)testXMLVersion { 47 | XCTAssertEqualObjects(self.document.version, @"1.0", @"XML version should be 1.0"); 48 | } 49 | 50 | - (void)testRootElement { 51 | XCTAssertEqualObjects(self.document.rootElement.tag, @"feed", @"root element should be feed"); 52 | // XCTAssertEqualObjects(self.document.rootElement[@"xmlns"], @"http://www.w3.org/2005/Atom", @"XML namespace should be Atom"); 53 | } 54 | 55 | - (void)testTitle { 56 | ONOXMLElement *titleElement = [self.document.rootElement firstChildWithTag:@"title"]; 57 | 58 | XCTAssertNotNil(titleElement, @"title element should not be nil"); 59 | XCTAssertEqualObjects(titleElement.tag, @"title", @"tag should be `title`"); 60 | XCTAssertEqualObjects([titleElement stringValue], @"Example Feed", @"title string value should be 'Example Feed'"); 61 | } 62 | 63 | - (void)testLinks { 64 | NSArray *linkElements = [self.document.rootElement childrenWithTag:@"link"]; 65 | 66 | XCTAssertTrue([linkElements count] == 2, @"should have 2 link elements"); 67 | XCTAssertEqualObjects([linkElements[0] stringValue], @"", @"stringValue should be nil"); 68 | XCTAssertNotEqualObjects(linkElements[0][@"href"], linkElements[1][@"href"], @"href values should not be equal"); 69 | } 70 | 71 | - (void)testUpdated { 72 | ONOXMLElement *updatedElement = [self.document.rootElement firstChildWithTag:@"updated"]; 73 | 74 | XCTAssertNotNil([updatedElement dateValue], @"dateValue should not be nil"); 75 | XCTAssertTrue([[updatedElement dateValue] isKindOfClass:[NSDate class]], @"dateValue should be kind of NSDate"); 76 | 77 | NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; 78 | dateComponents.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"]; 79 | dateComponents.year = 2003; 80 | dateComponents.month = 12; 81 | dateComponents.day = 13; 82 | dateComponents.hour = 18; 83 | dateComponents.minute = 30; 84 | dateComponents.second = 2; 85 | 86 | XCTAssertEqualObjects([updatedElement dateValue], [[NSCalendar calendarWithIdentifier:NSGregorianCalendar] dateFromComponents:dateComponents], @"dateValue should be equal to December 13, 2003 6:30:02 PM"); 87 | } 88 | 89 | - (void)testEntries { 90 | NSArray *entryElements = [self.document.rootElement childrenWithTag:@"entry"]; 91 | XCTAssertTrue([entryElements count] == 1, @"should be 1 entry element"); 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /Ono/ONOXMLDocument.h: -------------------------------------------------------------------------------- 1 | // ONOXMLDocument.h 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | 25 | @class ONOXMLElement; 26 | 27 | /** 28 | 29 | */ 30 | @protocol ONOSearching 31 | 32 | /** 33 | 34 | */ 35 | - (id )XPath:(NSString *)XPath; 36 | 37 | /** 38 | 39 | */ 40 | - (void)enumerateElementsWithXPath:(NSString *)XPath 41 | block:(void (^)(ONOXMLElement *element))block; 42 | 43 | /// 44 | 45 | /** 46 | 47 | */ 48 | - (id )CSS:(NSString *)CSS; 49 | 50 | /** 51 | 52 | */ 53 | - (void)enumerateElementsWithCSS:(NSString *)CSS 54 | block:(void (^)(ONOXMLElement *element))block; 55 | @end 56 | 57 | #pragma mark - 58 | 59 | /** 60 | 61 | */ 62 | @interface ONOXMLDocument : NSObject 63 | 64 | /** 65 | 66 | */ 67 | @property (readonly, nonatomic, copy) NSString *version; 68 | 69 | /** 70 | 71 | */ 72 | @property (readonly, nonatomic, assign) NSStringEncoding encoding; 73 | 74 | /** 75 | 76 | */ 77 | @property (readonly, nonatomic, strong) ONOXMLElement *rootElement; 78 | 79 | /// 80 | 81 | /** 82 | 83 | */ 84 | @property (readonly, nonatomic, strong) NSNumberFormatter *numberFormatter; 85 | 86 | /** 87 | 88 | */ 89 | @property (readonly, nonatomic, strong) NSDateFormatter *dateFormatter; 90 | 91 | /// 92 | 93 | /** 94 | 95 | */ 96 | + (instancetype)XMLDocumentWithString:(NSString *)string 97 | encoding:(NSStringEncoding)encoding 98 | error:(NSError * __autoreleasing *)error; 99 | 100 | /** 101 | 102 | */ 103 | + (instancetype)XMLDocumentWithData:(NSData *)data 104 | error:(NSError * __autoreleasing *)error; 105 | 106 | /// 107 | 108 | /** 109 | 110 | */ 111 | + (instancetype)HTMLDocumentWithString:(NSString *)string 112 | encoding:(NSStringEncoding)encoding 113 | error:(NSError * __autoreleasing *)error; 114 | 115 | /** 116 | 117 | */ 118 | + (instancetype)HTMLDocumentWithData:(NSData *)data 119 | error:(NSError * __autoreleasing *)error; 120 | 121 | @end 122 | 123 | #pragma mark - 124 | 125 | /** 126 | 127 | */ 128 | @interface ONOXMLElement : NSObject 129 | 130 | /** 131 | 132 | */ 133 | @property (readonly, nonatomic, strong) ONOXMLDocument *document; 134 | 135 | /** 136 | 137 | */ 138 | @property (readonly, nonatomic, copy) NSString *tag; 139 | 140 | /// 141 | 142 | /** 143 | 144 | */ 145 | @property (readonly, nonatomic, strong) NSDictionary *attributes; 146 | 147 | /** 148 | 149 | */ 150 | - (id)valueForAttribute:(NSString *)key; 151 | 152 | /** 153 | 154 | */ 155 | - (id)valueForAttribute:(NSString *)key 156 | inNamespace:(NSString *)namespace; 157 | 158 | /// 159 | 160 | /** 161 | 162 | */ 163 | @property (readonly, nonatomic, strong) ONOXMLElement *parent; 164 | 165 | /** 166 | 167 | */ 168 | @property (readonly, nonatomic, strong) NSArray *children; 169 | 170 | /** 171 | 172 | */ 173 | @property (readonly, nonatomic, strong) ONOXMLElement *previousSibling; 174 | 175 | /** 176 | 177 | */ 178 | @property (readonly, nonatomic, strong) ONOXMLElement *nextSibling; 179 | 180 | /** 181 | 182 | */ 183 | - (ONOXMLElement *)firstChildWithTag:(NSString *)tag; 184 | 185 | /** 186 | 187 | */ 188 | - (ONOXMLElement *)firstChildWithTag:(NSString *)tag 189 | inNamespace:(NSString *)namespace; 190 | 191 | /** 192 | 193 | */ 194 | - (NSArray *)childrenWithTag:(NSString *)tag; 195 | 196 | /** 197 | 198 | */ 199 | - (NSArray *)childrenWithTag:(NSString *)tag 200 | inNamespace:(NSString *)namespace; 201 | 202 | /// 203 | 204 | /** 205 | 206 | */ 207 | @property (readonly, nonatomic, assign, getter = isBlank) BOOL blank; 208 | 209 | /** 210 | 211 | */ 212 | - (NSString *)stringValue; 213 | 214 | /** 215 | 216 | */ 217 | - (NSNumber *)numberValue; 218 | 219 | /** 220 | 221 | */ 222 | - (NSDate *)dateValue; 223 | 224 | /// 225 | 226 | /** 227 | 228 | */ 229 | - (id)objectAtIndexedSubscript:(NSUInteger)idx; 230 | 231 | /** 232 | 233 | */ 234 | - (id)objectForKeyedSubscript:(id)key; 235 | 236 | @end 237 | 238 | 239 | /// 240 | 241 | /** 242 | 243 | */ 244 | extern NSString * const ONOErrorDomain; 245 | -------------------------------------------------------------------------------- /Example/nutrition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 65 5 | 20 6 | 300 7 | 2400 8 | 300 9 | 25 10 | 50 11 | 12 | 13 | Avocado Dip 14 | Sunnydale 15 | 29 16 | 17 | 11 18 | 3 19 | 5 20 | 210 21 | 2 22 | 0 23 | 1 24 | 25 | 0 26 | 0 27 | 28 | 29 | 0 30 | 0 31 | 32 | 33 | 34 | Bagels, New York Style 35 | Thompson 36 | 104 37 | 38 | 4 39 | 1 40 | 0 41 | 510 42 | 54 43 | 3 44 | 11 45 | 46 | 0 47 | 0 48 | 49 | 50 | 8 51 | 20 52 | 53 | 54 | 55 | Beef Frankfurter, Quarter Pound 56 | Armitage 57 | 115 58 | 59 | 32 60 | 15 61 | 65 62 | 1100 63 | 8 64 | 0 65 | 13 66 | 67 | 0 68 | 2 69 | 70 | 71 | 1 72 | 6 73 | 74 | 75 | 76 | Chicken Pot Pie 77 | Lakeson 78 | 198 79 | 80 | 22 81 | 9 82 | 25 83 | 810 84 | 42 85 | 2 86 | 10 87 | 88 | 20 89 | 2 90 | 91 | 92 | 2 93 | 10 94 | 95 | 96 | 97 | Cole Slaw 98 | Fresh Quick 99 | 1.5 100 | 101 | 0 102 | 0 103 | 0 104 | 15 105 | 5 106 | 2 107 | 1 108 | 109 | 30 110 | 45 111 | 112 | 113 | 4 114 | 2 115 | 116 | 117 | 118 | Eggs 119 | Goodpath 120 | 50 121 | 122 | 4.5 123 | 1.5 124 | 215 125 | 65 126 | 1 127 | 0 128 | 6 129 | 130 | 6 131 | 0 132 | 133 | 134 | 2 135 | 4 136 | 137 | 138 | 139 | Hazelnut Spread 140 | Ferreira 141 | 2 142 | 143 | 10 144 | 2 145 | 0 146 | 20 147 | 23 148 | 2 149 | 3 150 | 151 | 0 152 | 0 153 | 154 | 155 | 6 156 | 4 157 | 158 | 159 | 160 | Potato Chips 161 | Lees 162 | 28 163 | 164 | 10 165 | 3 166 | 0 167 | 180 168 | 15 169 | 1 170 | 2 171 | 172 | 0 173 | 10 174 | 175 | 176 | 0 177 | 0 178 | 179 | 180 | 181 | Soy Patties, Grilled 182 | Gardenproducts 183 | 96 184 | 185 | 5 186 | 0 187 | 0 188 | 420 189 | 10 190 | 4 191 | 9 192 | 193 | 0 194 | 0 195 | 196 | 197 | 0 198 | 0 199 | 200 | 201 | 202 | Truffles, Dark Chocolate 203 | Lyndon's 204 | 39 205 | 206 | 19 207 | 14 208 | 25 209 | 10 210 | 16 211 | 1 212 | 1 213 | 214 | 0 215 | 0 216 | 217 | 218 | 0 219 | 0 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /Example/Ono Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F897F97A18BFC44E0043A736 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F897F97918BFC44E0043A736 /* Foundation.framework */; }; 11 | F897F97D18BFC44E0043A736 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F897F97C18BFC44E0043A736 /* main.m */; }; 12 | F897F98B18BFC4CE0043A736 /* ONOXMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = F897F98A18BFC4CE0043A736 /* ONOXMLDocument.m */; }; 13 | F897F99018BFC5860043A736 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F897F98F18BFC5860043A736 /* libxml2.dylib */; }; 14 | F897F99D18BFCC9A0043A736 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F897F99C18BFCC9A0043A736 /* XCTest.framework */; }; 15 | F897F9A318BFCC9A0043A736 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F897F9A118BFCC9A0043A736 /* InfoPlist.strings */; }; 16 | F897F9A518BFCC9A0043A736 /* ONOXMLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F897F9A418BFCC9A0043A736 /* ONOXMLTests.m */; }; 17 | F897F9AD18BFCEB60043A736 /* atom.xml in Resources */ = {isa = PBXBuildFile; fileRef = F897F9AC18BFCEB60043A736 /* atom.xml */; }; 18 | F897F9AF18BFCED00043A736 /* ONOAtomTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F897F9AE18BFCED00043A736 /* ONOAtomTests.m */; }; 19 | F897F9B018BFCF8E0043A736 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F897F98F18BFC5860043A736 /* libxml2.dylib */; }; 20 | F897F9B118BFCFC20043A736 /* ONOXMLDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = F897F98A18BFC4CE0043A736 /* ONOXMLDocument.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | F897F9A718BFCC9A0043A736 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = F897F96E18BFC44E0043A736 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = F897F97518BFC44E0043A736; 29 | remoteInfo = "Ono Example"; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXCopyFilesBuildPhase section */ 34 | F897F97418BFC44E0043A736 /* CopyFiles */ = { 35 | isa = PBXCopyFilesBuildPhase; 36 | buildActionMask = 2147483647; 37 | dstPath = /usr/share/man/man1/; 38 | dstSubfolderSpec = 0; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 1; 42 | }; 43 | /* End PBXCopyFilesBuildPhase section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | F897F97618BFC44E0043A736 /* Ono Example */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "Ono Example"; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | F897F97918BFC44E0043A736 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 48 | F897F97C18BFC44E0043A736 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ../main.m; sourceTree = ""; }; 49 | F897F97F18BFC44E0043A736 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Prefix.pch; path = ../Prefix.pch; sourceTree = ""; }; 50 | F897F98818BFC4CE0043A736 /* Ono.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ono.h; sourceTree = ""; }; 51 | F897F98918BFC4CE0043A736 /* ONOXMLDocument.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ONOXMLDocument.h; sourceTree = ""; }; 52 | F897F98A18BFC4CE0043A736 /* ONOXMLDocument.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ONOXMLDocument.m; sourceTree = ""; }; 53 | F897F98D18BFC55B0043A736 /* nutrition.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = nutrition.xml; sourceTree = SOURCE_ROOT; }; 54 | F897F98F18BFC5860043A736 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; }; 55 | F897F99B18BFCC9A0043A736 /* Ono Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Ono Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | F897F99C18BFCC9A0043A736 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 57 | F897F9A018BFCC9A0043A736 /* Ono Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Ono Tests-Info.plist"; sourceTree = ""; }; 58 | F897F9A218BFCC9A0043A736 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 59 | F897F9A418BFCC9A0043A736 /* ONOXMLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ONOXMLTests.m; sourceTree = ""; }; 60 | F897F9AC18BFCEB60043A736 /* atom.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = atom.xml; sourceTree = ""; }; 61 | F897F9AE18BFCED00043A736 /* ONOAtomTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ONOAtomTests.m; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | F897F97318BFC44E0043A736 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | F897F99018BFC5860043A736 /* libxml2.dylib in Frameworks */, 70 | F897F97A18BFC44E0043A736 /* Foundation.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | F897F99818BFCC9A0043A736 /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | F897F9B018BFCF8E0043A736 /* libxml2.dylib in Frameworks */, 79 | F897F99D18BFCC9A0043A736 /* XCTest.framework in Frameworks */, 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | F897F96D18BFC44E0043A736 = { 87 | isa = PBXGroup; 88 | children = ( 89 | F897F97B18BFC44E0043A736 /* Example */, 90 | F897F99E18BFCC9A0043A736 /* Tests */, 91 | F897F97818BFC44E0043A736 /* Frameworks */, 92 | F897F97718BFC44E0043A736 /* Products */, 93 | F897F98C18BFC4D10043A736 /* Vendor */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | F897F97718BFC44E0043A736 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | F897F97618BFC44E0043A736 /* Ono Example */, 101 | F897F99B18BFCC9A0043A736 /* Ono Tests.xctest */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | F897F97818BFC44E0043A736 /* Frameworks */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | F897F98F18BFC5860043A736 /* libxml2.dylib */, 110 | F897F97918BFC44E0043A736 /* Foundation.framework */, 111 | F897F99C18BFCC9A0043A736 /* XCTest.framework */, 112 | ); 113 | name = Frameworks; 114 | sourceTree = ""; 115 | }; 116 | F897F97B18BFC44E0043A736 /* Example */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | F897F97C18BFC44E0043A736 /* main.m */, 120 | F897F98E18BFC5600043A736 /* Resources */, 121 | F897F97E18BFC44E0043A736 /* Supporting Files */, 122 | ); 123 | name = Example; 124 | path = "Ono Example"; 125 | sourceTree = ""; 126 | }; 127 | F897F97E18BFC44E0043A736 /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | F897F97F18BFC44E0043A736 /* Prefix.pch */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | F897F98718BFC4CE0043A736 /* Ono */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | F897F98818BFC4CE0043A736 /* Ono.h */, 139 | F897F98918BFC4CE0043A736 /* ONOXMLDocument.h */, 140 | F897F98A18BFC4CE0043A736 /* ONOXMLDocument.m */, 141 | ); 142 | name = Ono; 143 | path = ../Ono; 144 | sourceTree = ""; 145 | }; 146 | F897F98C18BFC4D10043A736 /* Vendor */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | F897F98718BFC4CE0043A736 /* Ono */, 150 | ); 151 | name = Vendor; 152 | sourceTree = ""; 153 | }; 154 | F897F98E18BFC5600043A736 /* Resources */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | F897F98D18BFC55B0043A736 /* nutrition.xml */, 158 | ); 159 | name = Resources; 160 | sourceTree = ""; 161 | }; 162 | F897F99E18BFCC9A0043A736 /* Tests */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | F897F9A418BFCC9A0043A736 /* ONOXMLTests.m */, 166 | F897F9AE18BFCED00043A736 /* ONOAtomTests.m */, 167 | F897F99F18BFCC9A0043A736 /* Supporting Files */, 168 | ); 169 | name = Tests; 170 | path = "Ono Tests"; 171 | sourceTree = ""; 172 | }; 173 | F897F99F18BFCC9A0043A736 /* Supporting Files */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | F897F9A018BFCC9A0043A736 /* Ono Tests-Info.plist */, 177 | F897F9A118BFCC9A0043A736 /* InfoPlist.strings */, 178 | F897F9AC18BFCEB60043A736 /* atom.xml */, 179 | ); 180 | name = "Supporting Files"; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXNativeTarget section */ 186 | F897F97518BFC44E0043A736 /* Ono Example */ = { 187 | isa = PBXNativeTarget; 188 | buildConfigurationList = F897F98418BFC44E0043A736 /* Build configuration list for PBXNativeTarget "Ono Example" */; 189 | buildPhases = ( 190 | F897F97218BFC44E0043A736 /* Sources */, 191 | F897F97318BFC44E0043A736 /* Frameworks */, 192 | F897F97418BFC44E0043A736 /* CopyFiles */, 193 | ); 194 | buildRules = ( 195 | ); 196 | dependencies = ( 197 | ); 198 | name = "Ono Example"; 199 | productName = "Ono Example"; 200 | productReference = F897F97618BFC44E0043A736 /* Ono Example */; 201 | productType = "com.apple.product-type.tool"; 202 | }; 203 | F897F99A18BFCC9A0043A736 /* Ono Tests */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = F897F9A918BFCC9A0043A736 /* Build configuration list for PBXNativeTarget "Ono Tests" */; 206 | buildPhases = ( 207 | F897F99718BFCC9A0043A736 /* Sources */, 208 | F897F99818BFCC9A0043A736 /* Frameworks */, 209 | F897F99918BFCC9A0043A736 /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | F897F9A818BFCC9A0043A736 /* PBXTargetDependency */, 215 | ); 216 | name = "Ono Tests"; 217 | productName = "Ono Tests"; 218 | productReference = F897F99B18BFCC9A0043A736 /* Ono Tests.xctest */; 219 | productType = "com.apple.product-type.bundle.unit-test"; 220 | }; 221 | /* End PBXNativeTarget section */ 222 | 223 | /* Begin PBXProject section */ 224 | F897F96E18BFC44E0043A736 /* Project object */ = { 225 | isa = PBXProject; 226 | attributes = { 227 | LastUpgradeCheck = 0500; 228 | ORGANIZATIONNAME = "Mattt Thompson"; 229 | TargetAttributes = { 230 | F897F99A18BFCC9A0043A736 = { 231 | TestTargetID = F897F97518BFC44E0043A736; 232 | }; 233 | }; 234 | }; 235 | buildConfigurationList = F897F97118BFC44E0043A736 /* Build configuration list for PBXProject "Ono Example" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | developmentRegion = English; 238 | hasScannedForEncodings = 0; 239 | knownRegions = ( 240 | en, 241 | ); 242 | mainGroup = F897F96D18BFC44E0043A736; 243 | productRefGroup = F897F97718BFC44E0043A736 /* Products */; 244 | projectDirPath = ""; 245 | projectRoot = ""; 246 | targets = ( 247 | F897F97518BFC44E0043A736 /* Ono Example */, 248 | F897F99A18BFCC9A0043A736 /* Ono Tests */, 249 | ); 250 | }; 251 | /* End PBXProject section */ 252 | 253 | /* Begin PBXResourcesBuildPhase section */ 254 | F897F99918BFCC9A0043A736 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | F897F9AD18BFCEB60043A736 /* atom.xml in Resources */, 259 | F897F9A318BFCC9A0043A736 /* InfoPlist.strings in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXResourcesBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | F897F97218BFC44E0043A736 /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | F897F97D18BFC44E0043A736 /* main.m in Sources */, 271 | F897F98B18BFC4CE0043A736 /* ONOXMLDocument.m in Sources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | F897F99718BFCC9A0043A736 /* Sources */ = { 276 | isa = PBXSourcesBuildPhase; 277 | buildActionMask = 2147483647; 278 | files = ( 279 | F897F9B118BFCFC20043A736 /* ONOXMLDocument.m in Sources */, 280 | F897F9A518BFCC9A0043A736 /* ONOXMLTests.m in Sources */, 281 | F897F9AF18BFCED00043A736 /* ONOAtomTests.m in Sources */, 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | }; 285 | /* End PBXSourcesBuildPhase section */ 286 | 287 | /* Begin PBXTargetDependency section */ 288 | F897F9A818BFCC9A0043A736 /* PBXTargetDependency */ = { 289 | isa = PBXTargetDependency; 290 | target = F897F97518BFC44E0043A736 /* Ono Example */; 291 | targetProxy = F897F9A718BFCC9A0043A736 /* PBXContainerItemProxy */; 292 | }; 293 | /* End PBXTargetDependency section */ 294 | 295 | /* Begin PBXVariantGroup section */ 296 | F897F9A118BFCC9A0043A736 /* InfoPlist.strings */ = { 297 | isa = PBXVariantGroup; 298 | children = ( 299 | F897F9A218BFCC9A0043A736 /* en */, 300 | ); 301 | name = InfoPlist.strings; 302 | sourceTree = ""; 303 | }; 304 | /* End PBXVariantGroup section */ 305 | 306 | /* Begin XCBuildConfiguration section */ 307 | F897F98218BFC44E0043A736 /* Debug */ = { 308 | isa = XCBuildConfiguration; 309 | buildSettings = { 310 | ALWAYS_SEARCH_USER_PATHS = NO; 311 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 312 | CLANG_CXX_LIBRARY = "libc++"; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BOOL_CONVERSION = YES; 315 | CLANG_WARN_CONSTANT_CONVERSION = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INT_CONVERSION = YES; 320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 321 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 322 | COPY_PHASE_STRIP = NO; 323 | GCC_C_LANGUAGE_STANDARD = gnu99; 324 | GCC_DYNAMIC_NO_PIC = NO; 325 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 326 | GCC_OPTIMIZATION_LEVEL = 0; 327 | GCC_PREPROCESSOR_DEFINITIONS = ( 328 | "DEBUG=1", 329 | "$(inherited)", 330 | ); 331 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 332 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 333 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 334 | GCC_WARN_UNDECLARED_SELECTOR = YES; 335 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 336 | GCC_WARN_UNUSED_FUNCTION = YES; 337 | GCC_WARN_UNUSED_VARIABLE = YES; 338 | MACOSX_DEPLOYMENT_TARGET = 10.9; 339 | ONLY_ACTIVE_ARCH = YES; 340 | SDKROOT = macosx; 341 | }; 342 | name = Debug; 343 | }; 344 | F897F98318BFC44E0043A736 /* Release */ = { 345 | isa = XCBuildConfiguration; 346 | buildSettings = { 347 | ALWAYS_SEARCH_USER_PATHS = NO; 348 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 349 | CLANG_CXX_LIBRARY = "libc++"; 350 | CLANG_ENABLE_OBJC_ARC = YES; 351 | CLANG_WARN_BOOL_CONVERSION = YES; 352 | CLANG_WARN_CONSTANT_CONVERSION = YES; 353 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 354 | CLANG_WARN_EMPTY_BODY = YES; 355 | CLANG_WARN_ENUM_CONVERSION = YES; 356 | CLANG_WARN_INT_CONVERSION = YES; 357 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | COPY_PHASE_STRIP = YES; 360 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 361 | ENABLE_NS_ASSERTIONS = NO; 362 | GCC_C_LANGUAGE_STANDARD = gnu99; 363 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | MACOSX_DEPLOYMENT_TARGET = 10.9; 371 | SDKROOT = macosx; 372 | }; 373 | name = Release; 374 | }; 375 | F897F98518BFC44E0043A736 /* Debug */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 379 | GCC_PREFIX_HEADER = Prefix.pch; 380 | HEADER_SEARCH_PATHS = ( 381 | "$(inherited)", 382 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 383 | "$(SDK_DIR)\"/usr/include/libxml2", 384 | ); 385 | PRODUCT_NAME = "$(TARGET_NAME)"; 386 | }; 387 | name = Debug; 388 | }; 389 | F897F98618BFC44E0043A736 /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 393 | GCC_PREFIX_HEADER = Prefix.pch; 394 | HEADER_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 397 | "$(SDK_DIR)\"/usr/include/libxml2", 398 | ); 399 | PRODUCT_NAME = "$(TARGET_NAME)"; 400 | }; 401 | name = Release; 402 | }; 403 | F897F9AA18BFCC9A0043A736 /* Debug */ = { 404 | isa = XCBuildConfiguration; 405 | buildSettings = { 406 | FRAMEWORK_SEARCH_PATHS = ( 407 | "$(DEVELOPER_FRAMEWORKS_DIR)", 408 | "$(inherited)", 409 | ); 410 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 411 | GCC_PREFIX_HEADER = Prefix.pch; 412 | GCC_PREPROCESSOR_DEFINITIONS = ( 413 | "DEBUG=1", 414 | "$(inherited)", 415 | ); 416 | HEADER_SEARCH_PATHS = ( 417 | "$(inherited)", 418 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 419 | "$(SDK_DIR)\"/usr/include/libxml2", 420 | ); 421 | INFOPLIST_FILE = "Ono Tests/Ono Tests-Info.plist"; 422 | PRODUCT_NAME = "$(TARGET_NAME)"; 423 | WRAPPER_EXTENSION = xctest; 424 | }; 425 | name = Debug; 426 | }; 427 | F897F9AB18BFCC9A0043A736 /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | FRAMEWORK_SEARCH_PATHS = ( 431 | "$(DEVELOPER_FRAMEWORKS_DIR)", 432 | "$(inherited)", 433 | ); 434 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 435 | GCC_PREFIX_HEADER = Prefix.pch; 436 | HEADER_SEARCH_PATHS = ( 437 | "$(inherited)", 438 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 439 | "$(SDK_DIR)\"/usr/include/libxml2", 440 | ); 441 | INFOPLIST_FILE = "Ono Tests/Ono Tests-Info.plist"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | WRAPPER_EXTENSION = xctest; 444 | }; 445 | name = Release; 446 | }; 447 | /* End XCBuildConfiguration section */ 448 | 449 | /* Begin XCConfigurationList section */ 450 | F897F97118BFC44E0043A736 /* Build configuration list for PBXProject "Ono Example" */ = { 451 | isa = XCConfigurationList; 452 | buildConfigurations = ( 453 | F897F98218BFC44E0043A736 /* Debug */, 454 | F897F98318BFC44E0043A736 /* Release */, 455 | ); 456 | defaultConfigurationIsVisible = 0; 457 | defaultConfigurationName = Release; 458 | }; 459 | F897F98418BFC44E0043A736 /* Build configuration list for PBXNativeTarget "Ono Example" */ = { 460 | isa = XCConfigurationList; 461 | buildConfigurations = ( 462 | F897F98518BFC44E0043A736 /* Debug */, 463 | F897F98618BFC44E0043A736 /* Release */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | F897F9A918BFCC9A0043A736 /* Build configuration list for PBXNativeTarget "Ono Tests" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | F897F9AA18BFCC9A0043A736 /* Debug */, 472 | F897F9AB18BFCC9A0043A736 /* Release */, 473 | ); 474 | defaultConfigurationIsVisible = 0; 475 | }; 476 | /* End XCConfigurationList section */ 477 | }; 478 | rootObject = F897F96E18BFC44E0043A736 /* Project object */; 479 | } 480 | -------------------------------------------------------------------------------- /Ono/ONOXMLDocument.m: -------------------------------------------------------------------------------- 1 | // ONOXMLDocument.m 2 | // 3 | // Copyright (c) 2014 Mattt Thompson (http://mattt.me/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import "ONOXMLDocument.h" 24 | 25 | #import 26 | #import 27 | #import 28 | 29 | NSString * ONOXPathFromCSS(NSString *CSS) { 30 | NSMutableArray *mutableXPathExpressions = [NSMutableArray array]; 31 | [[CSS componentsSeparatedByString:@","] enumerateObjectsUsingBlock:^(NSString *expression, NSUInteger idx, BOOL *stop) { 32 | if (expression && [expression length] > 0) { 33 | __block NSMutableArray *mutableXPathComponents = [NSMutableArray arrayWithObject:@"/"]; 34 | 35 | [[[expression stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] enumerateObjectsUsingBlock:^(NSString *token, NSUInteger idx, __unused BOOL *stop) { 36 | if ([token isEqualToString:@"*"] && idx != 0) { 37 | [mutableXPathComponents addObject:@"/*"]; 38 | } else if ([token isEqualToString:@">"]) { 39 | [mutableXPathComponents addObject:@""]; 40 | } else if ([token isEqualToString:@"+"]) { 41 | [mutableXPathComponents addObject:@"following-sibling::*[1]/self::"]; 42 | } else if ([token isEqualToString:@"~"]) { 43 | [mutableXPathComponents addObject:@"following-sibling::"]; 44 | } else { 45 | 46 | NSRange symbolRange = [token rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"#.[]"]]; 47 | if (symbolRange.location == NSNotFound) { 48 | [mutableXPathComponents addObject:token]; 49 | } else { 50 | NSMutableString *mutableXPathComponent = [NSMutableString stringWithString:[token substringToIndex:symbolRange.location]]; 51 | NSRange range = NSMakeRange(0, [token length]); 52 | 53 | { 54 | NSError *error = nil; 55 | NSRegularExpression *idRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\#(\\w+)" options:0 error:&error]; 56 | NSTextCheckingResult *result = [idRegularExpression firstMatchInString:CSS options:0 range:range]; 57 | if ([result numberOfRanges] > 1) { 58 | [mutableXPathComponent appendFormat:@"[@id = '%@']", [token substringWithRange:[result rangeAtIndex:1]]]; 59 | } 60 | } 61 | 62 | { 63 | NSError *error = nil; 64 | NSRegularExpression *classRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\.([^\\.]+)" options:0 error:&error]; 65 | for (NSTextCheckingResult *result in [classRegularExpression matchesInString:token options:0 range:range]) { 66 | if ([result numberOfRanges] > 1) { 67 | [mutableXPathComponent appendFormat:@"[contains(concat(' ',normalize-space(@class),' '),' %@ ')]", [token substringWithRange:[result rangeAtIndex:1]]]; 68 | } 69 | } 70 | } 71 | 72 | { 73 | NSError *error = nil; 74 | NSRegularExpression *attributeRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\[(\\w+)\\]" options:0 error:&error]; 75 | for (NSTextCheckingResult *result in [attributeRegularExpression matchesInString:token options:0 range:range]) { 76 | if ([result numberOfRanges] > 1) { 77 | [mutableXPathComponent appendFormat:@"[@%@]", [token substringWithRange:[result rangeAtIndex:1]]]; 78 | } 79 | } 80 | } 81 | 82 | [mutableXPathComponents addObject:mutableXPathComponent]; 83 | } 84 | } 85 | }]; 86 | 87 | [mutableXPathExpressions addObject:[mutableXPathComponents componentsJoinedByString:@"/"]]; 88 | } 89 | }]; 90 | 91 | return [mutableXPathExpressions componentsJoinedByString:@" | "]; 92 | } 93 | 94 | static BOOL ONOXMLNodeMatchesTagInNamespace(xmlNodePtr node, NSString *tag, NSString *namespace) { 95 | BOOL matchingTag = !tag || [[NSString stringWithUTF8String:(const char *)node->name] compare:tag options:NSCaseInsensitiveSearch] == NSOrderedSame; 96 | BOOL matchingNamespace = !namespace || [[NSString stringWithUTF8String:(const char *)node->ns->href] compare:namespace options:NSCaseInsensitiveSearch] == NSOrderedSame; 97 | 98 | return matchingTag && matchingNamespace; 99 | } 100 | 101 | @interface ONOXPathEnumerator : NSEnumerator 102 | @end 103 | 104 | @interface ONOXPathEnumerator () 105 | @property (readwrite, nonatomic, assign) xmlXPathObjectPtr xmlXPath; 106 | @property (readwrite, nonatomic, assign) NSUInteger cursor; 107 | @property (readwrite, nonatomic, strong) ONOXMLDocument *document; 108 | @end 109 | 110 | @interface ONOXMLElement () 111 | @property (readwrite, nonatomic, assign) xmlNodePtr xmlNode; 112 | @property (readwrite, nonatomic, strong) ONOXMLDocument *document; 113 | @end 114 | 115 | @interface ONOXMLDocument () 116 | @property (readwrite, nonatomic, assign) xmlDocPtr xmlDocument; 117 | @property (readwrite, nonatomic, strong) ONOXMLElement *rootElement; 118 | @property (readwrite, nonatomic, copy) NSString *version; 119 | @property (readwrite, nonatomic, strong) NSNumberFormatter *numberFormatter; 120 | @property (readwrite, nonatomic, strong) NSDateFormatter *dateFormatter; 121 | 122 | - (ONOXMLElement *)elementWithNode:(xmlNodePtr)node; 123 | - (ONOXPathEnumerator *)enumeratorWithXPathObject:(xmlXPathObjectPtr)XPath; 124 | @end 125 | 126 | #pragma mark - 127 | 128 | @implementation ONOXPathEnumerator 129 | 130 | - (void)dealloc { 131 | if (_xmlXPath) { 132 | xmlXPathFreeObject(_xmlXPath); 133 | } 134 | } 135 | 136 | - (id)objectAtIndex:(NSUInteger)idx { 137 | if (idx >= xmlXPathNodeSetGetLength(self.xmlXPath->nodesetval)) { 138 | return nil; 139 | } 140 | 141 | return [self.document elementWithNode:self.xmlXPath->nodesetval->nodeTab[idx]]; 142 | } 143 | 144 | #pragma mark - NSEnumerator 145 | 146 | - (NSArray *)allObjects { 147 | NSMutableArray *mutableObjects = [NSMutableArray arrayWithCapacity:(NSUInteger)self.xmlXPath->nodesetval->nodeNr]; 148 | for (NSInteger idx = 0; idx < xmlXPathNodeSetGetLength(self.xmlXPath->nodesetval); idx++) { 149 | ONOXMLElement *element = [self objectAtIndex:idx]; 150 | if (element) { 151 | [mutableObjects addObject:element]; 152 | } 153 | } 154 | 155 | return [NSArray arrayWithArray:mutableObjects]; 156 | } 157 | 158 | - (id)nextObject { 159 | if (self.cursor >= self.xmlXPath->nodesetval->nodeNr) { 160 | return nil; 161 | } 162 | 163 | return [self objectAtIndex:_cursor++]; 164 | } 165 | 166 | @end 167 | 168 | #pragma mark - 169 | 170 | @implementation ONOXMLDocument 171 | 172 | + (instancetype)XMLDocumentWithString:(NSString *)string 173 | encoding:(NSStringEncoding)encoding 174 | error:(NSError * __autoreleasing *)error 175 | { 176 | return [self XMLDocumentWithData:[string dataUsingEncoding:encoding] error:error]; 177 | } 178 | 179 | + (instancetype)XMLDocumentWithData:(NSData *)data 180 | error:(NSError * __autoreleasing *)error 181 | { 182 | xmlDocPtr document = xmlReadMemory([data bytes], (int)[data length], "", nil, XML_PARSE_RECOVER); 183 | if (!document) { 184 | return nil; 185 | } 186 | 187 | return [[self alloc] initWithDocument:document]; 188 | } 189 | 190 | + (instancetype)HTMLDocumentWithString:(NSString *)string 191 | encoding:(NSStringEncoding)encoding 192 | error:(NSError * __autoreleasing *)error 193 | { 194 | return [self HTMLDocumentWithData:[string dataUsingEncoding:encoding] error:error]; 195 | } 196 | 197 | + (instancetype)HTMLDocumentWithData:(NSData *)data 198 | error:(NSError * __autoreleasing *)error 199 | { 200 | xmlDocPtr document = htmlReadMemory([data bytes], (int)[data length], "", nil, HTML_PARSE_NOWARNING | HTML_PARSE_NOERROR); 201 | if (!document) { 202 | return nil; 203 | } 204 | 205 | return [[self alloc] initWithDocument:document]; 206 | } 207 | 208 | #pragma mark - 209 | 210 | - (instancetype)initWithDocument:(xmlDocPtr)document { 211 | self = [super init]; 212 | if (!self) { 213 | return nil; 214 | } 215 | 216 | _xmlDocument = document; 217 | if (self.xmlDocument) { 218 | self.rootElement = [self elementWithNode:xmlDocGetRootElement(self.xmlDocument)]; 219 | } 220 | 221 | return self; 222 | } 223 | 224 | - (void)dealloc { 225 | if (_xmlDocument) { 226 | xmlFreeDoc(_xmlDocument); 227 | } 228 | } 229 | 230 | #pragma mark - 231 | 232 | - (NSNumberFormatter *)numberFormatter { 233 | if (!_numberFormatter) { 234 | _numberFormatter = [[NSNumberFormatter alloc] init]; 235 | [_numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle]; 236 | } 237 | 238 | return _numberFormatter; 239 | } 240 | 241 | - (NSDateFormatter *)dateFormatter { 242 | if (!_dateFormatter) { 243 | _dateFormatter = [[NSDateFormatter alloc] init]; 244 | [_dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; 245 | [_dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; 246 | } 247 | 248 | return _dateFormatter; 249 | } 250 | 251 | #pragma mark - 252 | 253 | - (ONOXMLElement *)elementWithNode:(xmlNodePtr)node { 254 | if (!node) { 255 | return nil; 256 | } 257 | 258 | ONOXMLElement *element = [[ONOXMLElement alloc] init]; 259 | element.xmlNode = node; 260 | element.document = self; 261 | 262 | return element; 263 | } 264 | 265 | - (ONOXPathEnumerator *)enumeratorWithXPathObject:(xmlXPathObjectPtr)XPath { 266 | if (!XPath || xmlXPathNodeSetIsEmpty(XPath->nodesetval)) { 267 | return nil; 268 | } 269 | 270 | ONOXPathEnumerator *enumerator = [[ONOXPathEnumerator alloc] init]; 271 | enumerator.xmlXPath = XPath; 272 | enumerator.document = self; 273 | 274 | return enumerator; 275 | } 276 | 277 | #pragma mark - ONOSearching 278 | 279 | - (id )XPath:(NSString *)XPath { 280 | return [self.rootElement XPath:XPath]; 281 | } 282 | 283 | - (void)enumerateElementsWithXPath:(NSString *)XPath 284 | block:(void (^)(ONOXMLElement *))block 285 | { 286 | [self.rootElement enumerateElementsWithXPath:XPath block:block]; 287 | } 288 | 289 | - (id )CSS:(NSString *)CSS { 290 | return [self.rootElement CSS:CSS]; 291 | } 292 | 293 | - (void)enumerateElementsWithCSS:(NSString *)CSS 294 | block:(void (^)(ONOXMLElement *))block 295 | { 296 | [self.rootElement enumerateElementsWithCSS:CSS block:block]; 297 | } 298 | 299 | #pragma mark - 300 | 301 | - (NSString *)version { 302 | if (!_version) { 303 | self.version = [NSString stringWithUTF8String:(const char *)self.xmlDocument->version]; 304 | } 305 | 306 | return _version; 307 | } 308 | 309 | #pragma mark - NSObject 310 | 311 | - (NSString *)description { 312 | return [self.rootElement description]; 313 | } 314 | 315 | - (BOOL)isEqual:(id)object { 316 | if (self == object) { 317 | return YES; 318 | } 319 | 320 | if (![object isKindOfClass:[self class]]) { 321 | return NO; 322 | } 323 | 324 | return [self hash] == [object hash]; 325 | } 326 | 327 | - (NSUInteger)hash { 328 | return (NSUInteger)self.xmlDocument; 329 | } 330 | 331 | #pragma mark - NSCopying 332 | 333 | - (id)copyWithZone:(NSZone *)zone { 334 | ONOXMLDocument *document = [[[self class] allocWithZone:zone] init]; 335 | document.version = self.version; 336 | document.rootElement = self.rootElement; 337 | 338 | return document; 339 | } 340 | 341 | #pragma mark - NSCoding 342 | 343 | - (id)initWithCoder:(NSCoder *)decoder { 344 | self = [super init]; 345 | if (!self) { 346 | return nil; 347 | } 348 | 349 | self.version = [decoder decodeObjectForKey:NSStringFromSelector(@selector(version))]; 350 | self.rootElement = [decoder decodeObjectForKey:NSStringFromSelector(@selector(rootElement))]; 351 | 352 | return self; 353 | } 354 | 355 | - (void)encodeWithCoder:(NSCoder *)coder { 356 | [coder encodeObject:self.version forKey:NSStringFromSelector(@selector(version))]; 357 | [coder encodeObject:self.rootElement forKey:NSStringFromSelector(@selector(rootElement))]; 358 | } 359 | 360 | @end 361 | 362 | #pragma mark - 363 | 364 | @interface ONOXMLElement () 365 | @property (readwrite, nonatomic, copy) NSString *rawXMLString; 366 | @property (readwrite, nonatomic, copy) NSString *tag; 367 | @property (readwrite, nonatomic, strong) ONOXMLElement *parent; 368 | @property (readwrite, nonatomic, strong) NSArray *children; 369 | @property (readwrite, nonatomic, strong) ONOXMLElement *previousSibling; 370 | @property (readwrite, nonatomic, strong) ONOXMLElement *nextSibling; 371 | @property (readwrite, nonatomic, strong) NSDictionary *attributes; 372 | @property (readwrite, nonatomic, copy) NSString *stringValue; 373 | @property (readwrite, nonatomic, copy) NSNumber *numberValue; 374 | @property (readwrite, nonatomic, copy) NSDate *dateValue; 375 | @end 376 | 377 | @implementation ONOXMLElement 378 | 379 | - (NSString *)tag { 380 | if (!_tag) { 381 | self.tag = [NSString stringWithUTF8String:(const char *)self.xmlNode->name]; 382 | } 383 | 384 | return _tag; 385 | } 386 | 387 | #pragma mark - 388 | 389 | - (NSDictionary *)attributes { 390 | if (!_attributes) { 391 | NSMutableDictionary *mutableAttributes = [NSMutableDictionary dictionary]; 392 | xmlAttrPtr attribute = self.xmlNode->properties; 393 | while (attribute) { 394 | NSString *key = [NSString stringWithUTF8String:(const char *)attribute->name]; 395 | mutableAttributes[key] = [self valueForAttribute:key]; 396 | } 397 | 398 | self.attributes = [NSDictionary dictionaryWithDictionary:mutableAttributes]; 399 | } 400 | 401 | return _attributes; 402 | } 403 | 404 | - (id)valueForAttribute:(NSString *)key { 405 | id value = nil; 406 | const unsigned char *xmlValue = xmlGetProp(self.xmlNode, (const xmlChar *)[key cStringUsingEncoding:NSUTF8StringEncoding]); 407 | if (xmlValue) { 408 | value = [NSString stringWithUTF8String:(const char *)xmlValue]; 409 | xmlFree((void *)xmlValue); 410 | } 411 | 412 | return value; 413 | } 414 | 415 | - (id)valueForAttribute:(NSString *)key 416 | inNamespace:(NSString *)namespace 417 | { 418 | id value = nil; 419 | const unsigned char *xmlValue = xmlGetNsProp(self.xmlNode, (const xmlChar *)[key cStringUsingEncoding:NSUTF8StringEncoding], (const xmlChar *)[namespace cStringUsingEncoding:NSUTF8StringEncoding]); 420 | if (xmlValue) { 421 | value = [NSString stringWithUTF8String:(const char *)xmlValue]; 422 | xmlFree((void *)xmlValue); 423 | } 424 | 425 | return value; 426 | } 427 | 428 | #pragma mark - 429 | 430 | - (ONOXMLElement *)parent { 431 | if (!_parent) { 432 | self.parent = [self.document elementWithNode:self.xmlNode->parent]; 433 | } 434 | 435 | return _parent; 436 | } 437 | 438 | - (NSArray *)children { 439 | return [self childrenAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, NSIntegerMax)]]; 440 | } 441 | 442 | - (ONOXMLElement *)firstChildWithTag:(NSString *)tag { 443 | return [self firstChildWithTag:tag inNamespace:nil]; 444 | } 445 | 446 | - (ONOXMLElement *)firstChildWithTag:(NSString *)tag 447 | inNamespace:(NSString *)namespace 448 | { 449 | return [[self childrenAtIndexes:[self indexesOfChildrenPassingTest:^BOOL(xmlNodePtr node, BOOL *stop) { 450 | *stop = ONOXMLNodeMatchesTagInNamespace(node, tag, namespace); 451 | return *stop; 452 | }]] firstObject]; 453 | } 454 | 455 | - (NSArray *)childrenWithTag:(NSString *)tag { 456 | return [self childrenWithTag:tag inNamespace:nil]; 457 | } 458 | 459 | - (NSArray *)childrenWithTag:(NSString *)tag 460 | inNamespace:(NSString *)namespace 461 | { 462 | 463 | return [self childrenAtIndexes:[self indexesOfChildrenPassingTest:^BOOL(xmlNodePtr node, BOOL *stop) { 464 | return ONOXMLNodeMatchesTagInNamespace(node, tag, namespace); 465 | }]]; 466 | } 467 | 468 | - (NSArray *)childrenAtIndexes:(NSIndexSet *)indexes { 469 | NSMutableArray *mutableChildren = [NSMutableArray array]; 470 | 471 | xmlNodePtr cursor = self.xmlNode->children; 472 | NSUInteger idx = 0; 473 | while (cursor) { 474 | if ([indexes containsIndex:idx] && cursor->type == XML_ELEMENT_NODE) { 475 | [mutableChildren addObject:[self.document elementWithNode:cursor]]; 476 | } 477 | 478 | cursor = cursor->next; 479 | idx++; 480 | } 481 | 482 | return [NSArray arrayWithArray:mutableChildren]; 483 | } 484 | 485 | - (NSIndexSet *)indexesOfChildrenPassingTest:(BOOL (^)(xmlNodePtr node, BOOL *stop))block { 486 | if (!block) { 487 | return nil; 488 | } 489 | 490 | NSMutableIndexSet *mutableIndexSet = [NSMutableIndexSet indexSet]; 491 | 492 | xmlNodePtr cursor = self.xmlNode->children; 493 | NSUInteger idx = 0; 494 | BOOL stop = NO; 495 | while (cursor && !stop) { 496 | if (block(cursor, &stop)) { 497 | [mutableIndexSet addIndex:idx]; 498 | } 499 | 500 | cursor = cursor->next; 501 | idx++; 502 | } 503 | 504 | return mutableIndexSet; 505 | } 506 | 507 | - (ONOXMLElement *)previousSibling { 508 | if (!_previousSibling) { 509 | self.previousSibling = [self.document elementWithNode:self.xmlNode->prev]; 510 | } 511 | 512 | return _previousSibling; 513 | } 514 | 515 | - (ONOXMLElement *)nextSibling { 516 | if (!_nextSibling) { 517 | self.nextSibling = [self.document elementWithNode:self.xmlNode->next]; 518 | } 519 | 520 | return _nextSibling; 521 | } 522 | 523 | #pragma mark - 524 | 525 | - (BOOL)isBlank { 526 | return [[self stringValue] length] == 0; 527 | } 528 | 529 | - (NSString *)stringValue { 530 | if (!_stringValue) { 531 | xmlChar *key = xmlNodeGetContent(self.xmlNode); 532 | self.stringValue = key ? [NSString stringWithUTF8String:(const char *)key] : @""; 533 | xmlFree(key); 534 | } 535 | 536 | return _stringValue; 537 | } 538 | 539 | - (NSNumber *)numberValue { 540 | if (!_numberValue) { 541 | self.numberValue = [self.document.numberFormatter numberFromString:[self stringValue]]; 542 | } 543 | 544 | return _numberValue; 545 | } 546 | 547 | - (NSDate *)dateValue { 548 | if (!_dateValue) { 549 | self.dateValue = [self.document.dateFormatter dateFromString:[self stringValue]]; 550 | } 551 | 552 | return _dateValue; 553 | } 554 | 555 | #pragma mark - 556 | 557 | - (id)objectForKeyedSubscript:(id)key { 558 | return [self valueForAttribute:key]; 559 | } 560 | 561 | - (id)objectAtIndexedSubscript:(NSUInteger)idx { 562 | return self.children[idx]; 563 | } 564 | 565 | #pragma mark - NSObject 566 | 567 | - (NSString *)description { 568 | xmlBufferPtr buffer = xmlBufferCreate(); 569 | xmlNodeDump(buffer, self.xmlNode->doc, self.xmlNode, 0, false); 570 | NSString *rawXMLString = [NSString stringWithUTF8String:(const char *)xmlBufferContent(buffer)]; 571 | xmlBufferFree(buffer); 572 | 573 | return rawXMLString; 574 | } 575 | 576 | - (BOOL)isEqual:(id)object { 577 | if (self == object) { 578 | return YES; 579 | } 580 | 581 | if (![object isKindOfClass:[self class]]) { 582 | return NO; 583 | } 584 | 585 | return [self hash] == [object hash]; 586 | } 587 | 588 | - (NSUInteger)hash { 589 | return (NSUInteger)self.xmlNode; 590 | } 591 | 592 | #pragma mark - ONOSearching 593 | 594 | - (id )XPath:(NSString *)XPath { 595 | if (!XPath) { 596 | return nil; 597 | } 598 | 599 | ONOXPathEnumerator *enumerator = nil; 600 | xmlXPathContextPtr context = xmlXPathNewContext(self.xmlNode->doc); 601 | xmlXPathSetContextNode(self.xmlNode, context); 602 | if (context) { 603 | enumerator = [self.document enumeratorWithXPathObject:xmlXPathEvalExpression((xmlChar *)[XPath cStringUsingEncoding:NSUTF8StringEncoding], context)]; 604 | 605 | xmlXPathFreeContext(context); 606 | } 607 | 608 | return enumerator; 609 | } 610 | 611 | - (void)enumerateElementsWithXPath:(NSString *)XPath 612 | block:(void (^)(ONOXMLElement *element))block 613 | { 614 | if (!block) { 615 | return; 616 | } 617 | 618 | for (ONOXMLElement *element in [self XPath:XPath]) { 619 | block(element); 620 | } 621 | } 622 | 623 | - (id )CSS:(NSString *)CSS { 624 | return [self XPath:ONOXPathFromCSS(CSS)]; 625 | } 626 | 627 | - (void)enumerateElementsWithCSS:(NSString *)CSS 628 | block:(void (^)(ONOXMLElement *element))block 629 | { 630 | [self enumerateElementsWithXPath:ONOXPathFromCSS(CSS) block:block]; 631 | } 632 | 633 | 634 | #pragma mark - NSObject 635 | 636 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector { 637 | return [[self stringValue] methodSignatureForSelector:selector]; 638 | } 639 | 640 | - (void)forwardInvocation:(NSInvocation *)invocation { 641 | [invocation invokeWithTarget:[self stringValue]]; 642 | } 643 | 644 | #pragma mark - NSCopying 645 | 646 | - (id)copyWithZone:(NSZone *)zone { 647 | ONOXMLElement *element = [[[self class] allocWithZone:zone] init]; 648 | element.xmlNode = self.xmlNode; 649 | element.document = self.document; 650 | 651 | return element; 652 | } 653 | 654 | #pragma mark - NSCoding 655 | 656 | - (id)initWithCoder:(NSCoder *)decoder { 657 | self = [super init]; 658 | if (!self) { 659 | return nil; 660 | } 661 | 662 | self.tag = [decoder decodeObjectForKey:NSStringFromSelector(@selector(tag))]; 663 | self.attributes = [decoder decodeObjectForKey:NSStringFromSelector(@selector(attributes))]; 664 | self.stringValue = [decoder decodeObjectForKey:NSStringFromSelector(@selector(stringValue))]; 665 | self.children = [decoder decodeObjectForKey:NSStringFromSelector(@selector(children))]; 666 | 667 | return self; 668 | } 669 | 670 | - (void)encodeWithCoder:(NSCoder *)coder { 671 | [coder encodeObject:self.tag forKey:NSStringFromSelector(@selector(tag))]; 672 | [coder encodeObject:self.attributes forKey:NSStringFromSelector(@selector(attributes))]; 673 | [coder encodeObject:self.stringValue forKey:NSStringFromSelector(@selector(stringValue))]; 674 | [coder encodeObject:self.children forKey:NSStringFromSelector(@selector(children))]; 675 | } 676 | 677 | @end 678 | --------------------------------------------------------------------------------