so I have an XML file with data from a lot of instances of an object. I'm parsing this file, but only want the data with the element tag "Content"
I am using NSXMLParser, so I have the methods parserDidStartDocument, didStartElement, foundCharacters, and didEndElement
So here is my current implementation
In Header:
#property (strong) NSMutableArray* AssetJSONObjects;
In Implementation:
boo
l grabContent = NO;
- (void) parserDidStartDocument:(NSXMLParser *)parser {
NSLog(#"parserDidStartDocument");
self.AssetJSONObjects = [NSMutableArray new];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
NSLog(#"didStartElement --> %#", elementName);
if([elementName isEqual:#"Content"])
{
grabContent = YES;
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
NSLog(#"foundCharacters --> %#", string);
if(grabContent)
{
[self.AssetJSONObjects addObject:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
NSLog(#"didEndElement --> %#", elementName);
if(grabContent)
{
grabContent = NO;
}
}
- (void) parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"parserDidEndDocument");
}
So here is my question: is the way that I'm declaring/initializing my array, AssetJSONObjects legitimate? Is the way I'm initializing my bool grabContent legitimate? Is there a better way to grab data from specific tags?
Went the cheap route, just kept track of a global boolean and set it if the tag was found
relevant code:
BOOL grabContent = NO;
- (void) parserDidStartDocument:(NSXMLParser *)parser {
self.AssetJSONObjects = [NSMutableArray new];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if([elementName isEqual:#"Content"])
{
grabContent = YES;
}
}
-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(grabContent)
{
NSDictionary *JSONObject =
[NSJSONSerialization JSONObjectWithData: [string dataUsingEncoding:NSUTF8StringEncoding]
options: NSJSONReadingMutableContainers
error: nil];
if (JSONObject)
{
[self.AssetJSONObjects addObject:JSONObject];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if(grabContent)
{
grabContent = NO;
}
}
- (void) parserDidEndDocument:(NSXMLParser *)parser {
self.notifications = [self convertRawJSONToNotifications:self.AssetJSONObjects];
if(self.notifications != nil)
{
self.notificationCompletionHandler(self.notifications, self.numNewNotifications);
}
}
Related
I trying to parse an xml that has one root element with 2 child elements with the same name. However they keep getting concatenated together. How can i fix this? here is my code so far
-
(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
element = elementName;
if([element isEqualToString:#"bustime-response"]){
self.item = [[NSMutableDictionary alloc]init];
self.direction =[[NSMutableString alloc]init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if ([element isEqualToString:#"dir"]){
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[self.direction appendString:string];
}
}
Try this it works for me:
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
element = elementName;
if ([element isEqualToString:#"bustime-response"])
{
elementFound = YES;
if (self.item)
self.item = nil;
self.item = [[NSMutableDictionary alloc]init];
if (self.direction)
self.direction = nil;
self.direction =[[NSMutableString alloc]init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (elementFound)
{
if ([element isEqualToString:#"dir"]){
string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[self.direction appendString:string];
}
//Add another if with the tag you want to retrieve the data from here
}
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([element isEqualToString:#"bustime-response"])
{
elementFound = NO;
// self.receivedDataArray - main array to store all of your dictionary
[self.receivedDataArray addObject: self.direction];
[self.receivedDataArray addObject: self.item];
self.direction = nil;
self.item = nil;
}
}
As you see you have to add another BOOL variable elementFound and NSMutableArray receivedDataArray for storing all of the data. Give it a go and let me know is it work for you. Remember it's good to add NSLogs for debugging, it helps a lot.
I have need to parse the following xml from webservicex.net, for and iPhone App. I need to store the verse Verse and BibleWords into some NSArray/NSDictionary or something.
<string xmlns="http://www.webserviceX.NET">
<NewDataSet>
<Table>
<Book>1</Book>
<BookTitle>Genesis</BookTitle>
<Chapter>1</Chapter>
<Verse>1</Verse>
<BibleWords>In the beginning God created the heaven and the earth.</BibleWords>
</Table>
<Table>
<Book>1</Book>
<BookTitle>Genesis</BookTitle>
<Chapter>1</Chapter>
<Verse>2</Verse>
<BibleWords>And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.</BibleWords>
</Table>
<Table>
<Book>1</Book>
<BookTitle>Genesis</BookTitle>
<Chapter>1</Chapter>
<Verse>3</Verse>
<BibleWords>And God said, Let there be light: and there was light.</BibleWords>
</Table>
<Table>
<Book>1</Book>
<BookTitle>Genesis</BookTitle>
<Chapter>1</Chapter>
<Verse>4</Verse>
<BibleWords>And God saw the light, that it was good: and God divided the light from the darkness.</BibleWords>
</Table>
</NewDataSet>
</string>
I wrote the following code for parsing the data.
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:#"string"]) {
model = [[Model alloc]init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(!currentElementValue) {
currentElementValue = [[NSMutableString alloc]initWithString:string];
}
else {
[currentElementValue appendString:string];
}
NSLog(#"%#", currentElementValue);
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"NewDataSet"]) {
return;
}
if ([elementName isEqualToString:#"Table"]) {
[verses addObject:model];
model = nil;
}
else {
[model setValue:currentElementValue forKey:elementName]; //The exception break point hits here
}
currentElementValue = nil;
}
The currentElementValue is displayed correctly. model is and object of a class named Model, currentElementValue is a NSMutableString object verses is an NSMutableArray. I am a beginner in objective C and haven't done parsing before. The problem is that the method:
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
is not working as expected. The app got broken after hitting the exception break point that i have commented in the code. How can I get the value of Verse and BibleWords and store it to some NSArray/NSMutableArray/NSDictionary/NSMutableDictionary. Please ask if there is anything else I need to mention to see where the problem actually lies.
EDIT:
Tried with this:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
captureCharacters = NO;
[currentElementValue setString : #""];
if ([elementName isEqualToString:#"Table"]) { //set a break point here as well. The elementName is string and hence is not entering any of the conditions in this delegate method. Hence captureCharacters is always set to NO and nothing is working.
model = [[Model alloc]init];
}
else if ([elementName isEqualToString:#"Verse"]) {
capturingCharacters = yes;
}
else if ([elementName isEqualToString:#"BibleWords"]) {
capturingCharacters = yes;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(capturingCharacters) {
[currentElementValue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"Table"]) {
[array addObject : model];
model = nil;
}
else if ([elementName isEqualToString:#"Verse"]) { //set a break point here
model.Verse = currentElementValue; //not entering this condition
}
else if ([elementName isEqualToString:#"BibleWords"]) {
model.BibleWords = currentElementValue; //not entering this condition
}
}
It is not working and I am getting null values for model.Verse and model.BibleWords.
How does these methods work?
EDIT-2
Model Interface:
//model.h
#protocol HttpRequestHandler <NSObject>
- (void)returnFromSiteParsedData:(NSMutableArray *)parsedData;
#end
#interface Model : NSObject <NSURLConnectionDelegate, NSXMLParserDelegate>
{
NSString *urlToLoad;
NSMutableURLRequest *requestSiteToSendData;
NSURLConnection *connectionToSiteToSendData;
NSString *verseText;
int verseNumber;
}
#property(nonatomic, retain) NSString *verseText;
#property(nonatomic, assign) int verseNumber;
- (void)loadSiteToSendDataWithChapterNumber:(int)chapterNumber AndBookNumber:(int)bookNumber;
#property(nonatomic, retain)id<HttpRequestHandler>httpRequestHandlerDelegate;
#end
Model Implementation
//model.m
#implementation Model
#synthesize verseNumber;
#synthesize verseText;
- (void)loadSiteToSendDataWithChapterNumber:(int)chapterNumber AndBookNumber:(int)bookNumber {
urlToLoad = [[NSString alloc]initWithFormat:#"http://www.webservicex.net/BibleWebservice.asmx/GetBibleWordsByBookTitleAndChapter?BookTitle=Genesis&chapter=1"];
requestSiteToSendData = [NSMutableURLRequest requestWithURL:[[NSURL alloc]initWithString:urlToLoad] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
connectionToSiteToSendData = [[NSURLConnection alloc]initWithRequest:requestSiteToSendData delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// Parse the XML into a dictionary
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:data];
VBSParser *vbsParser = [[VBSParser alloc]initVBSParser];
[parser setDelegate:vbsParser];
BOOL success = [parser parse];
if (success) {
NSMutableArray *verses = [vbsParser verses];
NSLog(#"%#", verses);
NSLog(#"Number: %d\n Text: %#", verseNumber,verseText);
[self.httpRequestHandlerDelegate returnFromSiteParsedData:verses];
}
// Print the dictionary
}
#end
A delegate is used to return parsed data to the view controller.
Make a BOOL capturingCharacters;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
captureCharacters = NO;
[currentElementValue setString : #""];
if ([elementName isEqualToString:#"Table"]) {
model = [[Model alloc]init];
}
else if ([elementName isEqualToString:#"Verse"]) {
capturingCharacters = yes;
}
else if ([elementName isEqualToString:#"BibleWords"]) {
capturingCharacters = yes;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if(capturingCharacters) {
[currentElementValue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"Table"]) {
[array addObject : model];
model = nil;
}
else if ([elementName isEqualToString:#"Verse"]) {
model.Verse = currentElementValue;
}
else if ([elementName isEqualToString:#"BibleWords"]) {
model.BibleWords = currentElementValue;
}
}
I am working on an App, which makes a search on a private server and shows the results to the user. The problem is NSXLParser can not parse the special german and french characters. For example: it should be:(Geschäftsführer) -> what i get is: (äftsführer)
How can i fix this ?
here is my code:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"results"])
{
currentJob = [SearchResult alloc];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"jobTitle"])
{
currentJob.jobTitle = currentNodeContent;
}
if ([elementname isEqualToString:#"location"])
{
currentJob.shortAddress = currentNodeContent;
}
if ([elementname isEqualToString:#"companyName"])
{
currentJob.employer = currentNodeContent;
}
if ([elementname isEqualToString:#"results"])
{
[self.jobs addObject:currentJob];
currentJob = nil;
currentNodeContent = nil;
}
}
Any help would be much appreciated...
Thanks in advance
Your foundCharacters method should append the string to a NSMutableString object, because it can be called multiple times for a single value. In didStartElement, initialize a NSMutableString object (let's call it elementContentString), then do this:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)chars
{
[self.elementContentString appendString:chars];
}
And in your didEndElement you can get the content as a string. Note that you should make a non-mutable copy of it (so you don't overwrite it with the next element), using [NSString stringWithString:self.elementContentString].
i have the following XMLParser but when i try to run it, it doesn't work properly.
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementname isEqualToString:#"results"])
{
currentJob = [SearchResult alloc];
}
}
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementname isEqualToString:#"jobTitle"])
{
currentJob.jobTitle = currentNodeContent;
}
if ([elementname isEqualToString:#"location"])
{
currentJob.shortAddress = currentNodeContent;
}
if ([elementname isEqualToString:#"companyName"])
{
currentJob.employer = currentNodeContent;
}
if ([elementname isEqualToString:#"results"])
{
[self.jobs addObject:currentJob];
currentJob = nil;
currentNodeContent = nil;
}
}
AND here is my foundCharakter Method:
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = (NSMutableString *) [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}
The output doesn't start from the beginning, it starts from the middle of the String...
I just can not understand, why some results look nice where some others don't.
What am i doing wrong ? How can i parse an xml properly ?
Any help will be appreciated.
Thx in advance
I used the following code and works now:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
if(!elementContentString)
elementContentString = [[NSMutableString alloc] initWithString:string];
else
[elementContentString appendString:string];
}
I don't think there any problem in your code, it is working fine at my end. I am using the twitter api for getting xml and it is giving me proper output.
I have read many examples of how to get text out of xml files, but just don't get how to. Here is a sample xml file:
<?xml version="1.0" encoding="UTF-8"?>
<questions>
<set>
<question>Question</question>
<answer>Answer</answer>
</set>
</questions>
Using -(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI, what's the easiest way to get the values Question and Answer? I already have my parser delegate hooked up and all that blah.
For implementing NSXMLParser you need to implement delegate method of it.
First of all initiate NSXMLParser in this manner.
- (void)viewDidLoad {
[super viewDidLoad];
rssOutputData = [[NSMutableArray alloc]init];
//declare the object of allocated variable
NSData *xmlData=[[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:#""]];// URL that given to parse.
//allocate memory for parser as well as
xmlParserObject =[[NSXMLParser alloc]initWithData:xmlData];
[xmlParserObject setDelegate:self];
//asking the xmlparser object to beggin with its parsing
[xmlParserObject parse];
//releasing the object of NSData as a part of memory management
[xmlData release];
}
//-------------------------------------------------------------
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
attributes: (NSDictionary *)attributeDict
{
if( [elementName isEqualToString:#"question"])
{
strquestion = [[NSMutableString alloc] init];
}
}
//-------------------------------------------------------------
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// init the ad hoc string with the value
currentElementValue = [[NSMutableString alloc] initWithString:string];
} else {
// append value to the ad hoc string
[currentElementValue appendString:string];
}
NSLog(#"Processing value for : %#", string);
}
//-------------------------------------------------------------
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if( [elementName isEqualToString:#"question"])
{
[strquestion setString:elementName];
}
[currentElementValue release];
currentElementValue = nil;
}
The above delegate method is sent by a parser object to its delegate when it encounters an end of specific element. In this method didEndElement you will get value of question.
// This one called out when it hit a starting tag on xml in your case <question>
BOOL got = FALSE;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
{
if([elementName isEqualToString:#"question"])
{
got = TRUE;
}
}
// This is third one to called out which gives the end tag of xml in your case </question>
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
// This delegate is the second one to called out which gives the value of current read tag in xml
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if(got)
{
NSLog(#"your desired tag value %#",string);
got = FALSE;
}
}
You need to implement the method
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
Once you see the elements whose (inner)text you want to grab, set a flag in your program, and keep a string with the things that foundCharacters finds between the tags. Once you hit the didEndElement method, you can do what you want with the string and reset the flag.
For example
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (sawQuestion) {
// need to check here that self->myString has been initialized
[self->myString appendString:string];
}
}
and in didEndElement you can reset the flag sawQuestion
You have to implement the callbacks for NSXMLParserDelegate
The key ones are:
// called when it found an element - in your example, question or answer
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
// called when it hits the closing of the element (question or answer)
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI
// called when it found the characters in the data of the element
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
So, when you hit the elements, you can set state whether the parser is currently in the question or answer element (with an iVar) and then when you get called back with foundCharacters, based on the state you set when you hit the element, you know which variable (question or answer) to assign the data to.