简体   繁体   中英

Parse an XML file with same tags

I am having an XML file which needs to be parsed. I have parse an XML before but it was simpler. Because this file has similar tags, I am not able to figure out how to parse it.

The xml file looks like this:

<resource classname="Quote">
<field name="name">USD/KRW</field>
<field name="price">1104.319946</field>
<field name="symbol">KRW=X</field>
<field name="ts">1350544190</field>
<field name="type">currency</field>
<field name="utctime">2012-10-18T07:09:50+0000</field>
<field name="volume">-56144</field>
</resource>

I want to get the price field and add it to an array. I tried the code below from some online examples but it's showing all field's values and I want only the price field.

- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{

    if ([[attributeDict objectForKey:@"name"] isEqualToString:@"price"])
    {

        currencyValuesString = [[NSMutableString alloc]init];
    }

}


- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementname isEqualToString:@"field"]) 
    {
        [currencyValuesArray addObject:currencyValuesString];
    }
}

- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    currencyValuesString = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

There is multiple problems with your code:

  • You alloc/init currencyValuesString when you enter your <field name="price"> tag, but you never release it (which will leak if you are not using ARC), and more importantly you don't use this allocated instance, because in your -parser:foundCharacters: you replace the value of this variable with a different string.
  • Thus the previously allocated NSMutableString is totally useless. And moreover, in case your fields would contain multiple chucks of text instead of one ( <field name="price">1.234<b>€</b></field> for example), -parser:foundCharacters: would be called multiple times, but your code will always throw away the previous value and keep only the last chunk instead of adding them to the NSMutableString .
  • If fact, with your code there is no point having an NSMutableString as you don't use it as a mutable string but simply replace each value with a new one each time instead of appending it.

But also, each time you encounter a closing </field> tag, whatever this tag is, you perform [currencyValuesArray addObject:currencyValuesString]; but don't release the currencyValuesString (which is fine only if you use ARC, but will leak if you use Manual Reference Counting) and more importantly you will add this string to the array each time you encounter the closing </field> tag, whatever the name of the opening tag was. So you will add the price to your array as many times as you have </field> closing tags after your price.

So the right approach would be to:

  • Append the foundCharacters to your NSMutableString instead of replacing it
  • When you encounter the closing tag, add the price to the array but then release and set the price string back to nil immediately. This way the currencyValuesString will only be around between the <field name="price"> opening tag and its corresponding </field> closing tag, and not when parsing the other <field> tags
  • Only add the price to your array in the closing tag if the currencyValuesString is not nil

- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if ([[attributeDict objectForKey:@"name"] isEqualToString:@"price"])
    {
        currencyValuesString = [[NSMutableString alloc]init];
    }
}

- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    [currencyValuesString appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}

- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
    if ([elementname isEqualToString:@"field"] && (currencyValuesString != nil)) 
    {
        [currencyValuesArray addObject:currencyValuesString];
        [currencyValuesString release]; // Only needed if not using ARC. Remove this line if you use ARC
        currencyValuesString = nil; // so that the next closing tag won't readd the price
    }
}

try this :

in .h

NSXMLParser *rssParser;

NSDictionary *myAttrDict;

NSString *currentElement;

NSMutableArray *prizeArr;

in .m:

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
    if([elementName isEqualToString:@"field"])
    {        
        if([[attributeDict objectForKey:@"name"] isEqualToString:@"price"])
        {
            currentElement = @"price";
        }
        else{
            currentElement = nil;
        }
    }
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
    if([currentElement isEqualToString:@"price"])
    {
        [prizeArr addObject:string];
    }
    NSLog(@"prizeArr :%@",prizeArr);
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
    {
    }

- (void)parserDidEndDocument:(NSXMLParser *)parser
{
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM