I have some xml that looks like this:
<menu>
<day name="monday">
<meal name="BREAKFAST">
<counter name="Bread">
<dish>
<name>Plain Bagel
<info name="Plain Bagel">
<serving>1 Serving (90g)</serving>
<calories>200</calories>
<caloriesFromFat>50</caloriesFromFat>
</info>
</name>
</dish>
<dish>
<name>Applesauce Coffee Cake
<info name="Applesauce Coffee Cake">
<serving>1 Slice-Cut 12 (121g)</serving>
<calories>374</calories>
<caloriesFromFat>104</caloriesFromFat>
</info>
</name>
</dish>
</counter>
</meal>
</day>
</menu>
And now I am trying to get the number of tags that are under the info
tag which should be three for the first info
tag which has the attribute of Plain Bagel
.
Like I said I am using Hpple parser for iOS. Here is what I have and am trying but can't quite get it to work.
- (void)getData:(NSData*)factData {
TFHpple *Parser = [TFHpple hppleWithHTMLData:factData];
NSString *XpathQueryString = @"//day[@name='monday']/meal[@name='BREAKFAST']/counter[@name='Bread']/dish/name/info[@name='Plain Bagel']";
NSArray *Nodes = [Parser searchWithXPathQuery:XpathQueryString];
NSInteger count = Nodes.count;
NSLog(@"count: %ld", count);
for (TFHppleElement *element in Nodes) {
NSLog(@"count inside: %ld", element.children.count);
}
}
And the first count give 1. Which is right but count inside gives 7, which is where I get confused. And not sure why this happens. After I get inside the info
tag I want to loop through for each tag, serving, calories, and calories from fat and get each tags text. But Im not sure why it gives 7?
Thanks for the help in advance.
The issue is that you're using a HTML parser not an XML parser. From a HTML perspective, you have seven elements between the info
open and close tags:
serving
tagcalories
tagcaloriesFromFat
tagIf you iterate through the children
objects, you'll see precisely that.
If you want only the entries associated with tags, you can check to see if the node, has children of its own:
TFHpple *parser = [TFHpple hppleWithXMLData:factData];
NSString *xpathQueryString = @"//day[@name='monday']/meal[@name='BREAKFAST']/counter[@name='Bread']/dish/name/info[@name='Plain Bagel']";
NSArray *nodes = [parser searchWithXPathQuery:xpathQueryString];
for (TFHppleElement *element in nodes) {
for (TFHppleElement *child in element.children) {
if (child.children.count > 0) { // see if the child, itself, has children
NSLog(@" %@: '%@'", child.tagName, child.content);
}
}
}
Or you could use a predicate:
TFHpple *parser = [TFHpple hppleWithXMLData:factData];
NSString *xpathQueryString = @"//day[@name='monday']/meal[@name='BREAKFAST']/counter[@name='Bread']/dish/name/info[@name='Plain Bagel']";
NSArray *nodes = [parser searchWithXPathQuery:xpathQueryString];
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(TFHppleElement *node, NSDictionary *bindings) {
return node.children.count > 0;
}];
for (TFHppleElement *element in nodes) {
NSArray *filteredNodes = [element.children filteredArrayUsingPredicate:predicate];
for (TFHppleElement *child in filteredNodes) {
NSLog(@" %@: '%@'", child.tagName, child.content);
}
}
If you were using a proper XML parser (e.g. NSXMLParser
) you wouldn't deal with random characters in between the open and close tags.