iosobjective-cxmlhppletfhpple

Parse xml in Xcode with Hpple


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.


Solution

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

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