how to parse xml - iphone

i am having an output xml which looks something like this,
<?xml version="1.0"?>
<root>
<apple><apple1>A</apple1></apple>
<ball><ball1>B</ball1></ball>
<cat><cat1>C</cat1></cat>
<dog><dog1>D</dog1></dog>
<ele><ele1>E</ele1></ele>
<root>
i am using NSXmlParser, i m confused, is tgis a gud xml structure?>//
secondly i dont knw how to parse this xml , i mean how should i write condition in parser didSTart and didEndElement ???
i m not to able properly navigate through each node, from apple to ele. i just want to assign the returned value of say apple to a string.
same for each node.
i m so confused in if / else condition.
Suggestions are always appreciated
regards

You need to have exactly one root element, like so:
<?xml version="1.0"?>
<root>
<apple><apple1>A</apple1></apple>
<ball><ball1>B</ball1></ball>
<cat><cat1>C</cat1></cat>
<dog><dog1>D</dog1></dog>
<ele><ele1>E</ele1></ele>
</root>
I don't know about the specifics of the parser you are using, but this is an intrinsic XML issue.

You can use below sample code.
resultArray = [[NSMutableArray alloc] init];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:webData];
[parser setDelegate:self];
[parser parse];
delegate methods to parse XML data
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSLog(#"error occured %#",parseError);
}
- (void)parserDidStartDocument:(NSXMLParser *)parser{
NSLog(#"Did Start Document");
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{
self.currentElement = elementName;
if([elementName isEqualToString:#"root"] && !itemDictionary){
self.itemDictionary = [[NSMutableDictionary alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
[itemDictionary setObject:string forKey:currentElement];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if([elementName isEqualToString:#"root"] && itemDictionary)){
[resultArray addObject:itemDictionary];
currentItem = nil;
}
}
- (void)parserDidEndDocument:(NSXMLParser *)parser{
NSLog("After parsing : %#", resultArray);
}

itemDictionary is a NSMutableDictionary.
I have edited my previous answer. (currentItem is itemDictionary).
In this sample code, itemDictionary is to store all values with key (when we parsing the data).
itemDictionary - {
apple1 = A
ball1 = B
cat1 = C
dog1 = D
ele1 = E
}
//--------------------------
<?xml version="1.0"?>
<root>
<apple id='10'><apple1>A</apple1></apple>
<ball><ball1>B</ball1></ball>
<cat><cat1>C</cat1></cat>
<dog><dog1>D</dog1></dog>
<ele><ele1>E</ele1></ele>
<root>
In didStartElement method, you have to use below sample code.
if([elementName isEqualToString:#"apple"]){
NSString *attributeValue = [attributeDict objectForKey:#"id"];
NSLog("Attribute value : %#", attributeValue);
}
I hope, it will help you.

Related

Parsing XML file with same name attributes in iphone using NSXMLParser?

I am creating an iPhone App in which I have to consume values from a service url.The response from url is of XML format.XML file has same name attributes many times,and those attributes are not static.I mean the no of attributes may increase from time to time. So I couldn't understand how to consume that XML response in iPhone.
My XML response looks like this :
GetUserResponse xmlns="http://schemas.datacontract.org/2004/07"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CompanyList>
<User>
<Address>
<City>Alabaster</City>
<State>Alabama</State>
</Address>
<Employee>
<Name>DrJohn</Name>
</Employee>
</User>
<User>
<Address>
<City>SanFransisco</City>>
<State>California</State>
</Address>
<Employee>
<Name>DrWilliams</Name>
</Employee>
</User>
</CompanyList>
</GetUserResponse>
The thing is I couldn't say there will be specific number of tags as of 2 tags here.They may or may not increase from time to time.I think we should take something like count number of items and extract the values but couldn't understand how?
This is one of many ways to do it. You can simply get the attributeDict and parse all the values from it using key-value.
NSString *element;
- (void) viewDidLoad {
[super viewDidLoad];
cityArr = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
element = elementName;
if ([elementName isEqualToString:#“Address”]) {
// initialize stings to store values of child elements
cityStr = [NSMutableString alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([element isEqualToString:#“City”])
cityStr appendString:string];// Append Values
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#“Address”]) {
[cityArr addObject:cityStr]; // Add strings to array/dictionary and release string
}
}

Parsing this file XML with touchXML

I have this XML file:
<continents>
<continent1 id="EUR" name="EUROPE"></continent1>
<continent2 id="AME" name="AMERICA"></continent2>
</continents>
I tried make it with NSXMLParser, but I cannot show the results in the same time, I would try it with touchXML someone can help me?
thanks.
"Europe" and "America" are attributes of your tags. Attributes defined as "name" in the tag. You really should get familiar with NSXMLParser and the event driven method in which it parses. Basically you create an NSObject subclass to be the delegate of the NSXMLParser and as it encounters elements it calls delegate methods on your custom object. And this very simple XML is a perfect example to learn. For example.
This code uses ARC.
SimpleExampleParse.h:
#interface SimpleExampleParse : NSObject <NSXMLParserDelegate>
#property (strong, nonatomic) NSMutableArray *continents;
-(id)initWithString:(NSString *)stringToParse;
#end
SimpleExampleParse.m:
#import "SimpleExampleParse.h"
#implementation SimpleExampleParse{
NSXMLParser *myParser;
}
#synthesize continents;
-(id)initWithString:(NSString *)stringToParse{
if ((self = [super init])){
myParser = [[NSXMLParser alloc] initWithData:[stringToParse dataUsingEncoding:NSUTF8StringEncoding]];
myParser.delegate = self;
self.continents = [[NSMutableArray alloc] init];
[myParser parse];
}
return self;
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// Check to see if this element has an attribute "name".
NSString *name = [attributeDict objectForKey:#"name"];
// If name is not nil add it to our Array
if (name) [continents addObject:name];
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// We don't care your XML contains no content
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
// We don't care your XML contains no content
}
#end
This class is used like this:
NSString *stringToParse = #"<continents><continent1 id=\"EUR\" name=\"EUROPE\"></continent1><continent2 id=\"AME\" name=\"AMERICA\"></continent2></continents>";
SimpleExampleParse *parser = [[SimpleExampleParse alloc] initWithString:stringToParse];
NSLog(#"contenents %#",parser.continents);
The output from the log will be:
contenents (
EUROPE,
AMERICA
)
Please now that you have something working in front of you take the time to study it and examine how it works.

NSXMLParser replaces é characters with \U00e9

I'm using an xml parser, NSXMLParser to parse asn xml an return some url in an NSMutableArray.Everything is giong great except the fact that the french é is replaced by \U00e9.
Here is my code:
- (void)parseXMLFileAtURL:(NSString *)URL
{
NSURL *xmlURL = [NSURL URLWithString:URL];
xmlParser = [NSXMLParser alloc] initWithContentsOfURL:xmlURL];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[xmlParser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[xmlParser setShouldProcessNamespaces:NO];
[xmlParser setShouldReportNamespacePrefixes:NO];
[xmlParser setShouldResolveExternalEntities:NO];
[xmlParser parse];
}
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
currentElement = [elementName copy];
if ([elementName isEqualToString:#"catalogue"]) {
// clear out our story item caches...
currentCatalogue = [[Catalogue alloc] init];
}
if ([elementName isEqualToString:#"partenaire"]) {
// clear out our story item caches...
currentPartenaire = [[Partenaire alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"catalogue"]) {
// Add currentCatalogue to array
[catalogueList addObject: currentCatalogue];
NSString *urls=[catalogueList valueForKey:#"url"];
NSLog(#"Current catalogue: urls=%#", urls);
}
if ([elementName isEqualToString:#"partenaire"]) {
// Add currentPartenaire to array
[partenaireList addObject: currentPartenaire];
/*NSLog(#"Current partenaire: raison_sociale=%#, lat=%#, lng=%#", currentPartenaire.raison_sociale, currentPartenaire.lat, currentPartenaire.lng);*/
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// Catalogue setup
if ([currentElement isEqualToString:#"id_model"])
currentCatalogue.id_model = string;
if ([currentElement isEqualToString:#"url"])
{
if (currentCatalogue.url)
{
currentCatalogue.url = [NSString stringWithFormat: #"%#%#", currentCatalogue.url, string];
// NSLog(#"Valoare url in data handler %#", currentCatalogue.url);
}
else
currentCatalogue.url = string;
}
}
Anyone any idea how to fix this?
TESTED CODE : 100 % WORKS
NSString* inputString =[NSString stringWithFormat:#"\40_TTRS_Coup\u00e9_TTRS_Roadster_Tarifs_20110428.pdf"];
NSLog(#"inputString is: %# \n\n",inputString);
OUTPUT:
inputString is: _TTRS_Coupé_TTRS_Roadster_Tarifs_20110428.pdf
Assuming your XML has an explicit encoding, as follows:
<?xml version="1.0" encoding="UTF-8"?>
then there are 2 problems you should address first. These may or may not fix your direct problem, but if not they will make it easier to narrow in on the problem you are seeing. Here are the problems you should fix:
Remove the code that calls 2 different initializer methods on a single NSXMLParser instance. That will have undefined results and its impossible to know what is going on until you fix that.
Change how you implement the parser:foundCharacters: method. As NSXMLParser documentations states, this may be called multiple times for a given set of characters within an XML element. Instead of just accepting the string and storing its value away, your delegate class should have a mutable character buffer that you append to each time foundCharacters gets called. Then in parser:didElementEnd you can grab the contents of the buffer and do what you need to with that value.
Try it out with these fixes and see if it works. If not, update your post with a corrected version of your code and it might be more obvious what the problem is.

Parsing XML for iPhone (aMule downloads/ search results)

I am currently working on a software to control aMule status of my server through the iPhone,
i created a socket that spits out xml which should be parsed out, but because NSXMLParser is event-drive, i'm having problems in understanding how this could work...
I thought of this type of XML structure, if you have ideas of a better way to structure it please tell me!! :D
<root type="donwloads"> <-- specifies downloads or search results
<file name="Ubuntu_9_10.iso" status="[11,6%]" />
<file name="Fedora 12.iso" status="[56,2%]" />
</root>
What i was thinking is, as i want to put this in a tableview, most probably i will need a NSMutableArray with lots of NSDictionaries based on the results, every dict should be a file.. what do you guys propose?? how should i handle this situation?
Thanks
Write a parser class that turns nodes into Core Data managed objects and saves them to the managed object context, when a parser callback event is fired.
Use an NSFetchedResultsController to access the Core Data store. As the managed objects come in and are saved, the results controller updates the table view with whatever results it fetches.
An NSMutableArray of NSDictionary seems like a reasonable approach for your in-memory data structure.
You'll basically have a series of callbacks that build up that array as NSXMLParser runs through your XML file:
- (void) parseXML:(NSString *) filename {
NSURL *xmlURL = [NSURL fileURLWithPath:filename];
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
[xmlParser setDelegate:self];
[xmlParser parse];
// Check for errors.
NSError *errorCode = [xmlParser parserError];
if (errorCode) {
// handle error here
NSLog(#"%#", [errorCode localizedDescription]);
}
[xmlParser release];
}
And your main delegate:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
// If certain elements are found initialize the object
if ([elementName isEqualToString:"#file"]) {
NSMutableDictionary *currentFile = [[NSMutableDictionary alloc] init];
// Look through the attributes add stuff to your dictionary
// Add it to your array.
}
}
Since all of your data is returned in attributes you can do it this way. Otherwise you'd need to store the file and build it up (the foundCharacters delegate) finally adding it to your array when the file's tag occurs in the didEndElement delegate.
Thanks a lot for your answers :D fortunately i resolved the problem 10 minutes after :D
ill post what i did:
XML:
<root>
<downloads>
<file type="text" name="fdsdf" />
<file type="text" name="sdfsdfssds" />
</downloads>
</root>
NSXMLParser delegates:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict{
if([elementName isEqualToString:#"downloads"] || [elementName isEqualToString:#"results"]){
NSLog(#"starting or downloads or results");
if(xmlArray){
xmlArray= nil;
}
self.xmlArray= [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"file"]){
NSLog(#"found file...");
[self.xmlArray addObject:attributeDict];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if([elementName isEqualToString:#"downloads"] || [elementName isEqualToString:#"results"]){
if([elementName isEqualToString:#"downloads"]){
NSLog(#"downloads found: %#... reloading table", xmlArray);
}
}
}
I hope this can possibly help someone which has my same problem :D

How would you parse the integer out of this XML response in Obj C?

I am working with an API where I get a response back this this, and I want to parse the integer ID out of it:
<?xml version="1.0"?>
<trip>328925</trip>
How would you parse this? I have some really fragile code I want to get rid of, and I'd appreciate some advice:
if ([[response substringWithRange:NSMakeRange(0, 21)]
isEqualToString: #"<?xml version=\"1.0\"?>"]) {
self.tripId = [response substringWithRange:NSMakeRange(28, response.length-35)];
}
I don't think I need an XML parsing library for this task!
I would use an XML parser. Using an XML parser really is the best way to parse XML.
It's not that hard to do either:
// Parser Delegate
#interface ParserDelegate : NSObject {
int inTripElements;
NSMutableString* trip;
}
#property (readonly) NSMutableString* trip;
#end
#implementation ParserDelegate
#synthesize trip;
- (id) init {
if (![super init]) {
[self release];
return nil;
}
trip = [#"" mutableCopy];
return self;
}
- (void) parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"trip"]) {
++inTripElements;
}
}
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (inTripElements > 0) {
[trip appendString:string];
}
}
- (void) parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"trip"]) {
--inTripElements;
}
}
#end
// Parsing
NSString* toParse = #"<?xml version=\"1.0\"?>"
"<trip>328925</trip>";
NSData* data = [NSData dataWithBytes:[toParse UTF8String]
length:strlen([toParse UTF8String])];
ParserDelegate* parserDelegate = [[ParserDelegate alloc] init];
NSXMLParser* parser = [[NSXMLParser alloc] initWithData:data];
[parser setDelegate:parserDelegate];
[parser parse];
[parser release];
NSLog(#"trip=%#", parserDelegate.trip);
[parserDelegate release];
If you really don't want to use an XML Parser to create a proper object why not use the regular expression <trip>\d*</trip> to match the element? Then you can get the integer part by removing the start and end tag and parsing to ensure correct as with any string.
Here is a handy place for testing regular expressions; http://regexlib.com/RETester.aspx
Check out 'NSScanner'. It would be perfect for this.
You should use an XML library for this as there are many cases where the code will change
For example in this case what happens if
The <?xml declaration is not sent or the encoding changes from UTF-8
or somone adds a space before the trip element
In all these cases the provider of the file can say the file is correct
etc.
with the XML parsing all this has been done and your code is more robust
Also in this case the code to parse and find is quite simple.