I'm using NSXMLParser to parse an XML file. I'm doing this several times in my app for different purposes. I have one which parses annotations for a store locator, one that parses al the movies from a certain YouTube channel and the last one parses a self made XML file.
All parsers work great except the last one. For some kind of reason it parses i.e. the first node good, but then overwrites it.
Here is my parser code:
//Standard function parser: reading open tag
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"item"]) {
xmlArray = [[NSMutableDictionary alloc] init];
}
}
//Standard function parser: reading string
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"created_time"]){
[xmlArray setObject:string forKey:currentElement];
NSLog(#"test 1 %#", string);}
}
//Standard function parser: reading close tag
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"item"]) {
Post *newPost = [[Post alloc] init];
NSLog(#"test %#", xmlArray );
newPost.created_time = [xmlArray objectForKey:#"created_time"];
[containerArray addObject:newPost];
[xmlArray release];
}
}
The XML looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<items>
<item>
<created_time>test</created_time>
<message>blabla</message>
<picture>http://www.trentshow.com/images/tada.jpg</picture>
</item>
</items>
And the log outputs the following:
2011-06-10 10:43:24.446 TabbedCalculation[1502:207] test 1 test
2011-06-10 10:43:24.448 TabbedCalculation[1502:207] test 1
2011-06-10 10:43:24.449 TabbedCalculation[1502:207] test 1
2011-06-10 10:43:24.450 TabbedCalculation[1502:207] test {
"created_time" = "\n";
}
I'm not getting why the parser loops 3 times over the created_time node. Any help would be great!
Try by following solution -- should work.
foundCharacters can be called for multiple time for each key value --- basically you should append all found character list.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([currentElement isEqualToString:#"created_time"]){
NSString *valueKey = [xmlArray ValueForKey:forKey:currentElement];
if(nil != valueKey)
{
valueKey = [valueKey stringByAppendingString:string];
}else
{
valueKey = string;
}
[xmlArray setObject:valueKey forKey:currentElement];
NSLog(#"test 1 %#", string);}
}
Related
I want to set string "EMPTY"for empty tag XML in NSXMLParse. Exm: my xml:
<STATE>0</STATE>
<MEMO/>
In above XML, MEMO tag is empty. I want to when NSXMLParse parse into tag, if it is empty,get string "EMPTY" in label. I used bellow code to parse xml:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
NSLog(#"Did start element");
if ( [elementName isEqualToString:#"FILENAME"])
{
XML_FIELD = FILENAME_CLOUD2;
NSLog(#"found rootElement");
return;
}
else if ( [elementName isEqualToString:#"MEMO"])
{
NSLog(#"found rootElement");
XML_FIELD = MEMO;
return;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
NSLog(#"Did end element");
if ([elementName isEqualToString:#"FILENAME"])
{
NSLog(#"rootelement end");
}
[strMemoEmpty setString:#"EMPTY"];
XML_FIELD = UNIMPORTANT2;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
//NSString *tagName = #"USER_NAME";
if (XML_FIELD == FILENAME_CLOUD2)
{
NSLog(#"Value %#",string);
[FileCompletedArray insertObject:string atIndex:0];
}
else if (XML_FIELD == MEMO)
{
NSLog(#"Value %#",string);
if (string != nil) {
[strMemoEmpty setString:#""];
[strMemoEmpty appendString: string];
NSLog(#"foundResults: %#",strMemoEmpty);
}
[MemoArray insertObject:string atIndex:0];
}
}
I used above code but it not check MEMO tag is empty. It missed when parse. Do you have any suggestions? Thanks in advance
The construction of your code is a bit strange and messy, you should reorganise so:
didStartElement creates new storage for the starting element and sets any flags
foundCharacters doesn't have logic, it just appends the received string to a mutable buffer (it could be called multiple times for the same tag)
didEndElement contains the logic (and checks the length of the found characters and substitutes your empty value if required)
The way you have it now means that partial data may be saved in foundCharacters and, because that method isn't called for empty tags, some of your logic is being set but never actually saved.
iam developing one application.In that iam using the xml parsing.That xml file contain the 4 category tags and other tags.From the xml file i want to get the data of category tag.At the time of getting the category data,every time i print the array of category values.That show correctly.But after completion of parsing, another tag data will be append to last category name.My parsing code is like below.
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(self.presentvalue)
{
[presentvalue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if(![elementName compare:#"category"])
{
[categories addObject:presentvalue];
NSLog(#"%#",presentvalue);
NSLog(#"Number of elements %#",categories);
}
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"category"])
{
presentvalue=[NSMutableString string];
}
}
And my xml file content is like
<lesson>
<categories>
<category id="1">1</category>
<category id="2">2</category>
<category id="3">3</category>
</categories>
<references>
<reference id="1" type="youtube" categoryid="2" name="Boyles law">
<url><![CDATA[http://www.youtube.com/watch?v=J_I8Y-i4Axc]]>
</url>
</reference>
</references>
</lesson>
From this xml i got the array values like 1,2,3http://www.youtube.com/watch?v=J_I8Y-i4Axc.Like this i got the result.So please tell me how to get the category values without that extra data.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"category"]) {
NSLog(#"Node is found correctly");
if(presentvalue == nil)
presentvalue = [NSMutableString string];
else
[presentvalue setString:#""];
}
else {
presentvalue = nil;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[presentvalue appendString:string];
if (presentvalue)
{
[self.tempArray addObject:presentvalue]; // add this line. create a tempArray containing all the strings.
presentvalue = nil;
}
}
this should help u in your parsing. hope it helps. happy coding :)
place check in your xml parsing code. that if the starting/ending element name is category. add objects to array else do nothing. just passout
I think this line look strange
if(![elementName compare:#"category"])
Shouldn't it be like this? (without !)
if([elementName compare:#"category"])
edited
Try replacing this to the didEndElement
if([elementName isEqualToString:#"category"])
{
[categories addObject:presentvalue];
presentValue = [NSMutableString string];
NSLog(#"%#",presentvalue);
NSLog(#"Number of elements %#",categories);
}
Please anybody help me.i am in great trouble....Now my Problem is...
Using xml parser i have parsed some attribute and value of the attribute.
using this i am getting the element name:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes: (NSDictionary *)attributeDict
Now using this i am getting value of the attribute from the xml:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
NSLog(#"found characters: %# %#", currentElement,string);
suppose my xml data are something like that...
-<list>
−<ProductData HASH="838557843">
<id>1</id>
<productNumber>a91cc0f4c7</productNumber>
<name>Product 1</name>
<seoTitle>product-1</seoTitle>
<viewCount>0</viewCount>
<lowStock>0.0</lowStock>
<image>5e928bbae358c93caedf6115fa7d178b.jpg</image>
<dateCreated>2011-10-06T16:08:45</dateCreated>
<dateUpdated>2011-10-06T10:08:45</dateUpdated>
<isDummy>true</isDummy>
<isInventory>false</isInventory>
</ProductData>
-<ProductData HASH="681596439">
<id>2</id>
<productNumber>d8287e2e51</productNumber>
<name>Product 2</name>
<seoTitle>product-2</seoTitle>
−<description>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit,....</p>
</description>
<viewCount>0</viewCount>
<availableStock>100.0</availableStock>
<lowStock>0.0</lowStock>
<image>8bbd8dfff3cdd28285d07810a4fe7c32.jpg</image>
<basePrice>10.0</basePrice>
<costPrice>10.0</costPrice>
<height>1.0</height>
<width>1.0</width>
<depth>1.0</depth>
<weight>3.0</weight>
<status>A</status>
<quantityOrderMin>1.0</quantityOrderMin>
<productIsCall>false</productIsCall>
<quantityOrderMax>20.0</quantityOrderMax>
<priceByAttribute>false</priceByAttribute>
<dateCreated>2011-10-06T16:08:45</dateCreated>
<dateUpdated>2011-10-06T10:08:45</dateUpdated>
<isDummy>true</isDummy>
<isInventory>false</isInventory>
</ProductData>
</list>`
Now when i will get the id attribute then it will store in an array then all of the attribute after first id until next id i want to store the attribute and its value in the dictionary,when i will get second id then i want to continue previous process...then according to the value of id i want to display the value in a UIView or UITableView. its not neccesary where i want to display it.
My question is how can i store data in array and dictionary and display it any time in my viewcontroller. Please help me. its becoming a great trouble for me. if you can please give me a sample code as an example. please please anybody help me...
Thanks in Advance
If you change your XML to
<list>
<Object>
<id>1</id>
<product>name</product>
<image>imagename.jpg</image>
<dateCreated>date</dateCreated>
<productleft>10</productleft>
</Object>
<Object>
<id>2</id>
<product>name</product>
<image>imagename.jpg</image>
<dateCreated>date</dateCreated>
<productleft>30</productleft>
</Object>
</list>
Or if your XML looks something like above its easy to use SAX parser ( NSXMLParser you've used ). Otherwise if you want to stick to XML you have posted use DOM style parsing.
Here is the code for the XML after formatting using NSXMLParser.
// these are ivars
NSMutableArray * objectsArray;
NSMutableDictionary * productDict;
NSMutableString * currentString;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if([elementName isEqualToString:#"Object"])
{
objectsArray = [[NSMutableArray alloc] init];
productDict = [[NSMutableDictionary alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(!currentString)
{
currentString = [[NSMutableString alloc] init];
}
[currentString appendString:string];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"id"])
{
[productDict setObject:currentString forKey:#"id"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"product"])
{
[productDict setObject:currentString forKey:#"product"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"image"])
{
[productDict setObject:currentString forKey:#"image"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"dateCreated"])
{
[productDict setObject:currentString forKey:#"dateCreated"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"productleft"])
{
[productDict setObject:currentString forKey:#"productleft"];
[currentString release],currentString = nil;
return;
}
if([elementName isEqualToString:#"Object"])
{
[objectsArray addObject:productDict];
[productDict release],productDict = nil;
}
[currentString release], currentString = nil;
}
I'm newbie on iPhone development.I want to parse XML file like this to get attribute value.
<Root>
<element att_1 ="value1" att_2 ="value2" />
<element att_1 ="value1" att_2 ="value2" />
.
.
.
<Root/>
Normally XML file
<element att="..."> content <element/>
I use this at didEndElement
if([elementName isEqualToString:#"element"]){
// Do something
}
How to check empty tag ? Advice me Please.
Thank you.
sorry for my bad english.
=====================================================================
Now I have new problem.when I run the application(simulator) it stuck and show message"Thread1:Stopped at breakpoint" in didStartElement.I guess I do something wrong.
StudyList.xml
<StudyList>
<study description="20040311181130" modsInStudy="" pat_birth="19760822" pat_id="47-3450" pat_name="Ekachai Chaichanasiri" refPhy="" study_date="Sun Jan 11 00:03:00 ICT 2004" study_id="696122224" study_iuid="1.2.392.200036.9123.100.200.20040311181130"/>
<study description="XoranCAT Study" modsInStudy="" pat_birth="19821117" pat_id="63" pat_name="Wantana Areeprayolkij" refPhy="" study_date="Fri Jan 28 00:04:00 ICT 2005" study_id="" study_iuid="1.2.826.0.1.3680043.2.594.4041.16900.9840.32723.30648"/>
</StudyList>
Delegate
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"StudyList"]) {
//Initialize the array.
appDelegate.studyList = [[NSMutableArray alloc] init];
} else if ([elementName isEqualToString:#"study"]) {
//Initialize the study.
aStudy.patientBrith = [attributeDict objectForKey:#"pat_birth"] ;
aStudy.patientID = [attributeDict objectForKey:#"pat_id"];
aStudy.patientName = [attributeDict objectForKey:#"pat_name"];
aStudy.studyDate = [attributeDict objectForKey:#"study_date"];
NSLog(#"Patient name: %#",aStudy.patientName);
NSLog(#"Patient ID: %#",aStudy.patientID);
NSLog(#"Patient Birth: %#" ,aStudy.patientBrith);
NSLog(#"Study Date: %#" ,aStudy.studyDate);
}
NSLog(#"Process Element");
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
//Nothing to do because XML file dose not have text node
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
//End if XML file nothing to do.
if ([elementName isEqualToString:#"StudyList"]) {
return;
//Add study object in to array and release the object.
if ([elementName isEqualToString:#""]||elementName == nil) {
[appDelegate.studyList addObject:aStudy];
}
}
}
NSLog(#"Patient name: %#",aStudy.patientName);
NSLog(#"Patient ID: %#",aStudy.patientID);
NSLog(#"Patient Birth: %#" ,aStudy.patientBrith);
NSLog(#"Study Date: %#" ,aStudy.studyDate);
***Output of above is null.
What wrong,Please tell me in right way.
Retain the value of string in this function
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
In - (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
Check
if ([retainedString isEqualToString:#"" ] || [retainString == nil)
Then the value is empty.
I am new to iphone development.I am parsing an XML page and displaying the content in a tableview.In some block in the XML page , the child element is missing , i want to add a value o in my array, when the child element is not fond in the particular block.My XML file is like
<entry>
<id>xxx </id>
<title>xxxxx</title>
<gd:ratings numRaters="2"/>
</entry> ..................................>first block
<entry>
<id>xxx </id>
<title>xxxxx</title>
</entry> ....................................>Second block
<entry>
<id>xxx </id>
<title>xxxxx</title>
<gd:ratings numRaters="2"/>
</entry> .....................................>Third block
I am parsing the gd:ratings tag and display its attribute numRates value in a tableview. I am having an mutable array which stores the value of numRates.I have to add object "O" in the array when the second block is parsed because it doesnot contain gd:ratings tag.Since the value is present in a attribute tag, for retrieving the content and adding it to the mutable array is done in NSXML parser DidStartElement method.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"entry"]) {
entry = [[NSMutableDictionary alloc] init];
currentTitle = [[NSMutableString alloc] init];
currentDate = [[NSMutableString alloc] init];
NSLog(#"inside image1 ");
}else if([elementName isEqualToString:#"media:thumbnail"])
{ currentImage = [[NSMutableString alloc] init];
if(myUrl == nil){
myUrl = [NSString stringWithString:[attributeDict objectForKey:#"url"]];
}
[currentImage appendString:myUrl];
[stories1 addObject:currentImage];
myUrl=nil;
} else if([elementName isEqualToString:#"gd:rating"])
{
currentRating=[[NSMutableString alloc]init];
myRatings=[NSString stringWithString:[attributeDict objectForKey:#"numRaters"]];
[currentRating appendString:myRatings];
[stories2 addObject:currentRating];
}
}
Since there are 25 blocks of entry tags and in that 10 block doesnot have gd rating element .So the array stories2 array has only 15 values.So i cannot display it in the tableview in order.Is there any way out to retrieve the attribute tag in "found characters" method . Please help me out.Thanks.
You can use the didEndElement method: when the outer element ended, check if the child element was added to the array.
You could use an iVar BOOL entryHasRating and do the following:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"entry"]) {
entryHasRating = NO;
...
} else if ([elementName isEqualToString:#"gd:rating"]){
entryHasRating = YES;
...
}
}
Then when an element ends:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"entry"]&& !entryHasRating) {
[stories2 addObject:#"No Rating"];
entryHasRating = YES;
}
...
}
If you know all of the data fields that you'll be expecting, you could build a dictionary with placeholders for all of your expected values before you parse the XML; as you parse the XML just replace the placeholders with real values.