objective-cxmluitableviewnsxmlparsertbxml

Convert TBXML parser to NSXML parser


I am developing an app for iOS device in Xcode6 (just updated from xcode5) where at some point the user pushes a button and then a tableview is seen with information nicely incorporated in each cell, this information is details of a corresponding object, and that object was specified by a identifier numeric value when he/she pushed the button.

So basically, using segue method I capture a numeric value entered in a textfield by the user in a previous view (SecondViewController.m) then there is another view where only a button is seen, with that number as it's label. User pushes the button and a tableview pops in, showing the details of that object.

The data (details info) is retrieved from xml URL, Everything works fine using my project with TBXML as my parser.

But recently I tested the app on a real device (iphone5s) and by the time I push the button in order to see the tableview and my object details, it happens nothing, as if the button is not there, at least the functionality, but in the simulator works wonderful.

My boss told me to change my code to use NSXML parser instead of TBXML parser. But I've seen tutorials and I don't simply get it though.

Can someone help me translate my block of TBXML-code to be NSXML-code please. BTW the "object" is a tree, and the details are specific information of that tree, like humidity, taxonomy, height, temperature, etc.

here is a link for a XML url: http://papvidadigital.com/risi/?nid=83 (get info from object 83)

Is a very simple XML.

And here is the code involving the parsing of that xml.

//XML


//LOADING THE XML FILE
//create link




NSString *buildingURL = [NSString stringWithFormat:@"http://papvidadigital.com/risi/?nid=%@", _passingValueToTable];




NSURL *myUrl = [NSURL URLWithString:buildingURL];

//setting data
NSData *myData = [NSData dataWithContentsOfURL:myUrl];

TBXML *sourceXML = [[TBXML alloc] initWithXMLData:myData error:nil];


//EXTRACT ELEMENTS
TBXMLElement *rootElement = sourceXML.rootXMLElement;

TBXMLElement *datoElement = [TBXML childElementNamed:@"dato" parentElement:rootElement];



//EXTRACT ATTRIBUTES

//EXTRACT element

//NID
//TBXMLElement *nidElement = [TBXML childElementNamed:@"NID" parentElement:datoElement];
//NSString *nidElementString = [TBXML textForElement:nidElement];
//NSLog(@"NID: %@\n", [nidElementString lowercaseString]);

//taxonomia
TBXMLElement *taxonomiaElement = [TBXML childElementNamed:@"taxonomia" parentElement:datoElement];
NSString *taxonomiaElementString = [TBXML textForElement:taxonomiaElement];
NSLog(@"taxonomia: %@\n", [taxonomiaElementString lowercaseString]);



//diametro
TBXMLElement *diametroElement = [TBXML childElementNamed:@"diametro" parentElement:datoElement];

NSString *diametroElementString = [TBXML textForElement:diametroElement];
NSString *diametroElementText = [ NSString stringWithFormat:@"%@ cm", diametroElementString];

    NSLog(@"diametro: %@\n", [diametroElementString lowercaseString]);

//Verificar y validar icono correspondiente
NSString *ThumbImageDiametro;
NSInteger diametroElementNumber = [diametroElementString integerValue];
if(diametroElementNumber >= 30){
    ThumbImageDiametro = @"diametroalto.png";
}else if(diametroElementNumber >= 15 && diametroElementNumber < 30){
    ThumbImageDiametro = @"diametromedio.png";
}else if(diametroElementNumber < 15){
    ThumbImageDiametro = @"diametropequeño.png";
}

Sorry some terms are in spanish. Basically I parse the xml, found each child of "dato" and then save what's inside ">" and "<" as a string value so I can later put it in an object Array I have to put data into my cells. As you can see I do some if-else statements with my "diametroElementNumber", this is because the corresponding ThumbImageDiametro (image in corresponding cell) will change according to the "diametroElementNumber" value. (Tree diameter size). I used a simple cast to integer.

Here is a small example of my object array:

_Description = @[taxonomiaElementString,
                 PlantadoElementString,
                 diametroElementText];

And this is from my images object array (images in each cell):

_Images = @[@"taxonomia.png",
            @"fechadeplantacion.png",
            ThumbImageDiametro];

And this is my object Array of fixed Titles for each cell:

_Title = @[@"Taxonomía",
           @"Año de Plantado",
           @"Diámetro"];

And this is how I put data into each cell:

//Put data into CELLS
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{



    //for cells that have TableCell as identifier
    static NSString *CellIdentifier = @"TableCell";
    Cell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

    // Configure the cell...

    long row = [indexPath row];
    cell.TitleLabel.text = _Title[row];

    cell.DescriptionLabel.text = _Description[row];
    //put corresponding image
    cell.ThumbImage.image = [UIImage imageNamed:_Images[row]];

    [cell setBackgroundColor:[UIColor whiteColor]];

    return cell;

}

And finally some generic/default methods for the tableview:

//calculates and returns number of sections in tableview controller
- (NSInteger)numberOfSelectionsInTableView:(UITableView *)tableView
{
    return 1; //number of sections
}
//calculates and returns number of rows in the section
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _Title.count;
}

Please Someone that can help me change what is necessary in order to stop using an external TBXML.h & TBXML.m file to then parse with, and use instead a NSXML parser to do the same as mentioned.

Basically I just want to have the translated code for this.

Thank you in advance


Solution

  • If you need to convert your code to the NSXML parser, then the documentation for the NSXMLParser class and the NSXMLParserDelegate protocol is you best start.

    To break it down to the basics, you can essentially follow these steps to get a good start:

    1. Announce that your class (likely a view controller?) conforms to the NSXMLParserDelegate protocol by putting it in your class header.
    2. Create an instance variable (or better yet, a property) of an NSXMLParser. You can even pull down the data right from the URL like so:

      self.treeParser = [[NSXMLParser alloc] initWithContentsOfURL:myUrl];

    3. Make sure to set self as the delegate so you can catch the delegate callbacks. And you can handle additional features here too, such as resolving external entities, etc.

      self.treeParser.delegate = self; self.treeParser.shouldResolveExternalEntities = YES;

    4. Handle the NSXMLParserDelegate methods that you want, most likely:

      • parser:didStartElement:...,
      • parser:didEndElement:..., `
      • parser:foundCharacters:
      • parser:parseErrorOccurred:

    The delegate methods listed above do most of the work. You can create instance variables (or properties) for currently parsed objects and store them when parsing is completed. When constructing consistent objects such as your tree data, it is probably best to create a custom class. So for your example XML, you could have a Tree class with taxonomia and plantado etc. properties to store the data from the service. You could then follow the basic parsing pattern

    1. In didStartElement:, if the element is "dato", then you want to alloc/init your custom Tree class and store it to an instance or property, currentTree perhaps.
    2. In foundCharacters:, you store the incoming characters to an instance or property, currentString perhaps.
    - (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
            if (!self.currentString) {
                // currentString is an NSMutableString typed property
                self.currentString = [[NSMutableString alloc] initWithCapacity:50];
            }
            [self.currentString appendString:string];
        }
    
    1. In didEndElement:, if the element is "taxonomia" or "plantado" or any other field you want to store to your class, you then assign the currentString to that class member. You could also use KVC (assuming your class complies and matches exactly to the xml fields) to do the following:

      [self.currentTree setValue:self.currentString forKey:elementName];

    This will save you the trouble of making several repetitive statements like

    if ([elementName isEqualToString:@"taxonomia"]) {...}
    

    You'll want to set currentString to nil here as well so you don't end up creating one long mutable string! Also, if in didEndElement the elementName is "dato", then you know the object has completed so you can add that to a collection or do other actions there.

    You should, of course, implement parseErrorOccurred: to handle any errors you may hit.

    Those are the basics. You can expand with more features of the NSXMLParser class and delegate protocols. Your tableview will only need some minor adjustments to work with your Tree class objects, and it will be well worth it.