parse multiple elements of xml in objective-c - objective-c

I need to parse multiple elements (id1 and id2) in such kind of xml:
<name>
<id1>104634449</id2>
<id2>22014870</id2>
</name>
<name>
<id1>104634433</id2>
<id2>220143210</id2>
</name>
I use such code:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI: (NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
isName = [elementName isEqualToString:#"name"];
isId1 = [elementName isEqualToString:#"id1"];
isId2 = [elementName isEqualToString:#"id2"];
if(isName){
name = [Name new];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if (isName){
[names addObject:name];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (isId1){
[name setId1:string];
}
if (isId2){
[name setId2:string];
}
}
But only one element (id1 or id2) is set in object name. What I do wrong?
In debug I get this:
id1 = #"\n"
id2 = #"22014870"

The problem is that when you finish to read id2 isName is set to NO, so you don't really add it to your array.
This is the sequence of actions:
The name element starts;
The id1 element starts;
The id1 element ends;
The id2 element starts;
The id2 element ends;
The name element ends.
At point 4 occurs the last assignment of isName, set to:
isName = [elementName isEqualToString:#"name"];
Which will result false.
The corrected code:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString: #"name"){
[names addObject:name];
}
}

Before you get too deep in the bowels of XML parsing I would highly recommend using GDataXMLNode (also available as a CocoaPod)
Then, to get an idea of how to use it, check out this tutorial, How To Read and Write XML Documents with GDataXML
I know this doesn't directly answer your question but it is really so much simpler to use this library provided by Google.

Related

Decode percent escaped string inside NSXMLParser delegate method

i'm getting a UTF8 encoded data from a server data is like Less%20than%20100 but i need this data in Less than 100 (decoded format),my NSXMLParsing delegate method is like
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if([elementName isEqualToString:#"option"]) {
dict = [[NSMutableDictionary alloc]init];
[dict setValue:[attributeDict objectForKey:#"text"] forKey:#"text"];/* Here itself i need to decode & save in to my dict */
}
How to decode this data.
After you have decoded your xml, use stringByReplacingPercentEscapesUsingEncoding: method on NSString :
NSString *decoded = [text stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
See the docs here.

How to strip the string to get the URL in the HTML <img>?

I am writing a RSS project on iPhone. I wonder how I can strip everything inside this tag just to get the image URL:
<description><![CDATA[ <img src="http://vnexpress.net/Files/Subject/3b/bd/67/91/Lat-xe-xang-2.jpg">Trưa 7/5, chiếc xe bồn chở 25.000 lít xăng từ Hà Tĩnh vào Quảng Bình bị lật nghiêng trên quốc lộ 1A khiến xăng chảy lênh láng ra đường. ]]></description>
I just want to get the string
http://vnexpress.net/Files/Subject/3b/bd/67/91/Lat-xe-xang-2.jpg
Please show me how to do it in this case?
I recommend NSXMLParser.
See here: http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html
use this method...
- (void)parser:(NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
NSString *someString = [[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding];
}
hoping this helps..
Edit:
Oh, #Ankit Srivastava answer is right. I didn't catch that is a CDATA block. Follow his answer, but I don't delete mine, couse this still can be useful to you.
You should implement NSXMLParser delegate. In your .h file <NSXMLParserDelegate>
And then you have to put 3 useful method in your m file.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (element == nil) // where element is a NSMutableString
element = [[NSMutableString alloc] init];
[element appendString:string];
}
That doesn't matte rif you have or it will always call didStart end didEnd.
Now we have ex: :
elementName //contains your tag name. In this case "mytag"
element // contains text beetween tags. In this case null. But in <mytag>something</mytag> element = something.
attributeDict //This is probably most important to you. That contains NSDictionary with all your parameters. In above example this is a dictionary with key: myparam and object: test

count elements in xml using objective c

I've got an XML file and I need to count how many times the element appears in it using Objective-C. How should I do this?
<?xml version="1.0" encoding="ISO-8859-1"?>
<residents>
<resident id="1">
<name>
<first>David</first>
<last>Dollar</last>
</name>
</resident>
<resident id="2">
<name>
<first>Michael</first>
<last>Nipp</last>
</name>
</resident>
etc...
I would set your class as the delegate of the parser, then this class will receive parse events such as parser:didStartElement:, parser:foundCharacters: and parser:didEndElement:.
self.parser = [[NSXMLParser alloc] initWithData:xmlData];
[self.parser setDelegate:self];
[self.parser parse];
I would create a count variable in your parser delegate. Anytime an element is found, the didStartElement: function is called on the parser delegate. Check if it's the "resident" element and increment the count if so.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"resident"]) {
self.count += 1;
}
}

NSXMLParser to create nodes and NSMutableArray

i'm looking for a advise. I want to parse an XML file with NSXMLParser, and i wonder what i should do with tags and parameters.
Fo example i have:
<template>
<template name="default" layout="absolute">
<image tmpl="topbanner"/>
<list tmpl="list">
<font tmpl="listfont"/>
<item target="target1">
<text>Target1</text>
</item>
<item target="target2">
<text>Target2</text>
</item>
<item target="target3">
<text>Target3</text>
</item>
.
.
.
And later i want to create an objects base on this information. So - where should i store retrieve information from parser? In method:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = [NSMutableString string];
}
I see that i can simply recieve atributes ang tags, but should I in this point write it in NSMutableArray or NSDictionary?
I've read NSXMLParser how to pass the NSMutableDictionary to a NSMutableArray But is it the best way?
If you know that a <list> element contains an array of subelements, you'll probably want to create an array or dictionary at the start of that block:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"list"]) {
self.list = [NSMutableDictionary dictionary];
self.listName = [attributeDict objectForKey:#"tmpl"]
}
else if ([elementName isEqualToString:#"item"]) {
self.itemKey = [attributeDict objectForKey:#"target"];
}
else if ([elementName isEqualToString:#"text"]) {
self.data = [NSMutableString string];
}
else if ([elementName isEqualToString:#"font"]) {
self.font = [attributeDict objectForKey:#"tmpl"];
}
}
Then you can add the simple <item> elements in your -parser:didEndElement:... method:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if (elementName isEqualToString:#"text") {
// no action needed here -- data already contains the text
}
else if (elementName isEqualToString:#"item"]) {
[self.list setObject:[self.data copy] forKey:self.itemKey;
self.itemKey = nil;
}
else if ([elementName isEqualToString:#"list"]) {
// do something appropriate with the list
[self.template setObject:self.items forKey:self.listName];
self.listName = nil;
self.list = nil;
}
}
This all presumes that you've got properties for font, data, itemKey, and so on... basically whatever you need to remember all the state you need until you can create the related object. Once you have the data you need, usually in the didEnd method, create the object, store it somewhere, and clear out the saved data.
This isn't the only approach to parsing the data. For example, you might want to go with a stack-based approach instead. But the idea illustrated above is probably the easiest to understand, and if the data isn't too complicated it's not hard to manage.
To get what's inside the tag use:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
[element appendString:string];
}
Then check in :
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
To get the attributes :
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"image"]) {
NSString *imageName = [attributeDict objectForKey:#"tmpl"];
}
}

Parsing XML using NSXML for iPhone

I have an XML which has a structure like this:
<data>
<books>
<item>
<title>book1</title>
<author>author1</author>
</item>
<item>
<title>book2</title>
<author>author2</author>
</item>
</books>
<magazines>
<item>
<title>Mag1</title>
<author>author1</author>
</item>
</magazines>
<journals>
<item>
<title>Journal1</title>
<author>author1</author>
</item>
<item>
<title>Journal2</title>
<author>author2</author>
</item>
</journals>
</data>
I am using a table view to display the items of each category. I plan to have a table view with columns books, magazines & journals and clicking on them should display only the items under them. That is, if I click on books, it should only display the book entries.
I have this for xml parsing
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *) elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqual:#"books"]) {
flag=1;
}
if ([elementName isEqual:#"title"]) {
NSLog(#"found title!");
parsedString = [[NSMutableString alloc] init];
}
if ([elementName isEqual:#"author"]) {
NSLog(#"description found");
parsedString = [[NSMutableString alloc] init];
}
}
and in
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (flag==1){
if ([elementName isEqual:#"title"]) {
[headlines addObject:parseString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"author"]) {
[authors addObject:parsedString];
[parseString release];
parseString = nil;
flag=2;
}
}
}
But this gets only the first entry of books and nothing else.
Instead of setting flags is there a way to parse only between the <books> and
</books> tags ?
Thank you.
The reason it is only getting the first item is that you are setting flag to 2 when you see the end element for the author tag. I suspect you will get closer to what you want if you update this flag in the end element for the books tag, as follows:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (flag==1){
if ([elementName isEqual:#"title"]) {
[headlines addObject:parseString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"author"]) {
[authors addObject:parsedString];
[parseString release];
parseString = nil;
}
if ([elementName isEqual:#"books"]) {
flag=2;
}
}
}
Just cancel parsing when the books tag ends:
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName isEqual:#"books"]) {
[parser abortParsing];
}
....
}