iOS - parse xml DATASET - objective-c

This is my XML data that I need to parse
<GetMessagesResult>
<NewDataSet xmlns="">
<Table>
<Date>21:52:59</Date>
<Message>ABC</Message>
<GroupName>ALL</GroupName>
</Table>
<Table>
<Date>11:23:27</Date>
<Message>DEF</Message>
<GroupName>ALL</GroupName>
</Table>
</NewDataSet>
</GetMessagesResult>
This is my SCMessages.h file
#import <Foundation/Foundation.h>
#interface SCMessages : NSObject
{
NSDate *Date;
NSString *Message;
NSString *GroupName;
}
#property (nonatomic, retain) NSDate *Date;
#property (nonatomic, retain) NSString *Message;
#property (nonatomic, retain) NSString *GroupName;
and this is my SCMessages.m file
#import "SCMessages.h"
#implementation SCMessages
#synthesize Date;
#synthesize Message,GroupName;
- (void)dealloc
{
[super dealloc];
[Date release];
[Message release];
[GroupName release];
}
#end
I used below code to parse the data using NSXMLParser delegate methods
#pragma mark - NSXMLParser Delegate
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *) namespaceURI qualifiedName:(NSString *)qName
attributes: (NSDictionary *)attributeDict
{
if( [elementName isEqualToString:#"GetMessagesResult"])
{
if(!soapResults)
soapResults = [[NSMutableString alloc] init];
recordResults = TRUE;
}
if([elementName isEqualToString:#"NewDataSet"]) {
//Initialize the array.
messages = [[NSMutableArray alloc] init];
}
else if([elementName isEqualToString:#"Table"]) {
//Initialize the message.
aMessage = [[SCMessages alloc] init];
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if( recordResults )
[soapResults appendString: string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:#"Table"]) {
[messages addObject:aMessage];
NSLog(#"MESSAGES COUNT: %d",messages.count);
NSLog(#"MESSAGE: %#",aMessage);
[aMessage release];
aMessage = nil;
}
// as this is the last element
if( [elementName isEqualToString:#"NewDataSet"])
{
recordResults = FALSE;
}
}
PROBLEM I am not getting the desired message object with Date, Meesage & GroupName.
I put NSLog to print them but I always get null value. The weird thing is message array gets allocated memory & also elements gets added to array as I can see message array count in NSLog but the array element has data as null value.
I am parsing the XML data received in SOAP response in NSURLConnection delegate method connectionDidFinishLoading
NSString *theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSUTF8StringEncoding];
NSLog(#"theXML: \n%#",theXML);

You need to store your values in aMessage. You have added particular object to array but you forgot to store data in that object of SCMessages class.You can also check while debug that you are getting value in aMessage object.
You need to store value like this in this didEndElement method.
[aMessage setValue: soapResults forKey:elementName];
I think this will help you.

Your didEnd method is missing code to store the accumulated string into a property. You need an ivar for the current SCMessage object.
Append text content to a mutable string ivar. Then on closing an element you have to decide which property to set on SCMessage. You probably have to parse the date into an NSDate.

Related

I Got the XML data ,Now how I can save the XML Data in Objective C?

As an New to the iOS Development in Xcode 7.
Below I have some questions please clarify my doubt.
I need to Store the XML Data from an URL
And Split the XML data each line and store it in the Objective C.
<?xml version="1.0" encoding="UTF-8"?>
<NewDataSet>
<tbl>
<Es_Id>8e268283-e87e-4abc-aab9-07cb611a8e60</Es_Id>
<EstablishmentType>40640054-2221-4086-92e4-4440497ccea2</EstablishmentType>
<EstablishmentName>La Parrilla Colombian Steakhouse & Bar</EstablishmentName>
<BusinessName>La Parrilla Colombian Steakhouse & Bar</BusinessName>
<OpenTime>PT8H31M</OpenTime>
<ClosingTime>PT18H50M</ClosingTime>
<Floor>12 th floor</Floor>
</tbl>
</NewDataSet>
When I start learning XML parsing it has basic steps.I implement all for you.
In ViewController.h
Step 1 : Add the Delegate classes
First you have to add <NSXMLParserDelegate>
Step 2 : Create necessary objects
NSXMLParser *parser;
NSMutableData *ReceviedData;
NSMutableString *currentStringValue;
NSMutableArray *arrayID;
Now it looks like
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<NSXMLParserDelegate>
{
NSXMLParser *parser;
NSMutableData *ReceviedData;
NSMutableString *currentStringValue;
NSMutableArray *arrayID;
}
#end
Then in ViewController.m
Step 3 - Allocate your Array in your viewDidLoad method
arrayID = [[NSMutableArray alloc]init];
Step 4 - Create Connection in your viewDidLoad Like
[self createConnection:#"http://www.google.com"]; //give your valid url.
Now the viewDidLoad method is
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
arrayID = [[NSMutableArray alloc]init];
[self createConnection:#"http://www.google.com"]; //give your valid url.
}
createConnection method is
-(void)createConnection:(NSString *)urlString
{
NSURL *url = [NSURL URLWithString:urlString];
// Step 5 - parser delegate methods are using NSURLConnectionDelegate class or not.
BOOL success;
if (!parser)
{
parser = [[NSXMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
parser.shouldResolveExternalEntities = YES;
success = [parser parse];
NSLog(#"Success : %c",success);
}
}
STEP 6 - NSXMLParserDlegate Methods are below
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
NSLog(#"Current Element Name : %#",elementName);
if ([elementName isEqualToString:#"ID"]) //according to your xml response your id is Es_Id.So you need to compare #"Es_Id"
{
NSLog(#"The Result is==%#",elementName);
}
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentStringValue = [[NSMutableString alloc] initWithString:string];
NSLog(#"Current String Value : %#",currentStringValue);
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:#"ID"]) //according to your xml response your id is Es_Id.So you need to compare #"Es_Id"
{
[arrayResult addObject:currentStringValue];
}
currentStringValue = nil;
}
From above code I check ID only.According to your response you need to compare other keys like EstablishmentType,EstablishmentName,BusinessName,OpenTime.......

Cannot set iVars in XMLParser

I've made an XMLParser class in objective-c and I can't seem to set the iVars in my shared store within the parser process, I've tried numerous ways but I'm getting nowhere.
This is my code and what is being returned, Here's hoping it's a small syntax error I've overlooked.
.h
#interface XMLParser : NSXMLParser <NSXMLParserDelegate>
{
XMLParser *XMLStore;
}
#property(nonatomic, weak)NSMutableString *longitudeValue;
#property(nonatomic, weak)NSMutableString *latitudeValue;
+ (XMLParser *)sharedStore;
- (void)parseXMLAtURL:(NSURL *)url;
#end
.m
#import "XMLParser.h"
#implementation XMLParser
BOOL blockLatLong = NO;
NSMutableString *currentNodeContent;
+ (XMLParser *)sharedStore
{
static XMLParser *XMLStore = nil;
if (!XMLStore)
XMLStore = [[XMLParser alloc] init];
return XMLStore;
}
- (void)parseXMLAtURL:(NSURL *)url
{
NSXMLParser *parser = [[XMLParser alloc] initWithContentsOfURL:url];
parser.delegate = self;
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
NSLog(#"Long:%#, Lat:%#", XMLStore.longitudeValue, XMLStore.latitudeValue);
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
currentNodeContent = [NSMutableString stringWithString:string];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"geometry"]){
blockLatLong = YES;
}
if ([elementName isEqualToString:#"location_type"]){
blockLatLong = NO;
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if (blockLatLong){
if ([elementName isEqualToString:#"lng"]){
[XMLStore setLongitudeValue:currentNodeContent];
NSLog(#"%#", currentNodeContent);
NSLog(#"%#", XMLStore.longitudeValue);
}
if ([elementName isEqualToString:#"lat"]){
[XMLStore setLatitudeValue:currentNodeContent];
NSLog(#"%#", currentNodeContent);
NSLog(#"%#", XMLStore.latitudeValue);
}
}
}
#end
Log
2013-09-23 11:19:59.606 Weathalert[640:c07] 40.7143528
2013-09-23 11:19:59.606 Weathalert[640:c07] (null)
2013-09-23 11:19:59.607 Weathalert[640:c07] -74.0059731
2013-09-23 11:19:59.607 Weathalert[640:c07] (null)
2013-09-23 11:19:59.607 Weathalert[640:c07] Long:(null), Lat:(null)
Your problem is that you've got three instances of XMLParser where you could be setting the instance variable:
Local NSXMLParser *parser allocated inside parseXMLAtURL:,
Function-static static XMLParser *XMLStore allocated inside sharedStore, and
Instance variable XMLParser *XMLStore; which you never allocate, so it stays nil.
It is the third instance on which you try calling your setters. Since it's nil, the calls have no effect: [XMLStore setLongitudeValue:...] does nothing.
To fix this, drop the second and the third variables, along with the +(XMLParser *)sharedStore method. Use the regular instance properties, rather than accessing the shared one.
You can harvest the results from the local parser variable upon completion of the [parser parse] call:
NSLog(#"Long:%#, Lat:%#", parser.longitudeValue, parser.latitudeValue);

Xml to dictionary parsing using XML reader [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I want to convert data in to dictionary ,any suggestions..
if you are newbie and don't know how to parse xml to dictionary... try with the below methods...
in .h file add this methods
#import <Foundation/Foundation.h>
#interface XMLReader : NSObject
{
NSMutableArray *dictionaryStack;
NSMutableString *textInProgress;
NSError **errorPointer;
}
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)errorPointer;
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)errorPointer;
#end
and in your .m file parse your URL using these methods.
NSString *const kXMLReaderTextNodeKey = #"text";
#interface XMLReader (Internal)
- (id)initWithError:(NSError **)error;
- (NSDictionary *)objectWithData:(NSData *)data;
#end
#implementation XMLReader
#pragma mark -
#pragma mark Public methods
+ (NSDictionary *)dictionaryForXMLData:(NSData *)data error:(NSError **)error
{
XMLReader *reader = [[XMLReader alloc] initWithError:error];
NSDictionary *rootDictionary = [reader objectWithData:data];
[reader release];
return rootDictionary;
}
+ (NSDictionary *)dictionaryForXMLString:(NSString *)string error:(NSError **)error
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [XMLReader dictionaryForXMLData:data error:error];
}
#pragma mark -
#pragma mark Parsing
- (id)initWithError:(NSError **)error
{
if (self = [super init])
{
errorPointer = error;
}
return self;
}
- (void)dealloc
{
[dictionaryStack release];
[textInProgress release];
[super dealloc];
}
- (NSDictionary *)objectWithData:(NSData *)data
{
// Clear out any old data
[dictionaryStack release];
[textInProgress release];
dictionaryStack = [[NSMutableArray alloc] init];
textInProgress = [[NSMutableString alloc] init];
// Initialize the stack with a fresh dictionary
[dictionaryStack addObject:[NSMutableDictionary dictionary]];
// Parse the XML
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
parser.delegate = self;
BOOL success = [parser parse];
// Return the stack's root dictionary on success
if (success)
{
NSDictionary *resultDict = [dictionaryStack objectAtIndex:0];
return resultDict;
}
return nil;
}
#pragma mark -
#pragma mark NSXMLParserDelegate methods
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
// Get the dictionary for the current level in the stack
NSMutableDictionary *parentDict = [dictionaryStack lastObject];
// Create the child dictionary for the new element, and initilaize it with the attributes
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict addEntriesFromDictionary:attributeDict];
// If there's already an item for this key, it means we need to create an array
id existingValue = [parentDict objectForKey:elementName];
if (existingValue)
{
NSMutableArray *array = nil;
if ([existingValue isKindOfClass:[NSMutableArray class]])
{
// The array exists, so use it
array = (NSMutableArray *) existingValue;
}
else
{
// Create an array if it doesn't exist
array = [NSMutableArray array];
[array addObject:existingValue];
// Replace the child dictionary with an array of children dictionaries
[parentDict setObject:array forKey:elementName];
}
// Add the new child dictionary to the array
[array addObject:childDict];
}
else
{
// No existing value, so update the dictionary
[parentDict setObject:childDict forKey:elementName];
}
// Update the stack
[dictionaryStack addObject:childDict];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
// Update the parent dict with text info
NSMutableDictionary *dictInProgress = [dictionaryStack lastObject];
// Set the text property
if ([textInProgress length] > 0)
{
[dictInProgress setObject:textInProgress forKey:kXMLReaderTextNodeKey];
// Reset the text
[textInProgress release];
textInProgress = [[NSMutableString alloc] init];
}
// Pop the current dict
[dictionaryStack removeLastObject];
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// Build the text value
[textInProgress appendString:string];
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
// Set the error pointer to the parser's error object
*errorPointer = parseError;
}
#end
I think this would be helpful to you for parsing the xml data.

iOS: Parsing a xml from HTTP using NSXMLParser for

It's first time using NSXMLParser and wondering if you give me some direction of parsing the returned xml from an http request that looks like:
<?xml version="1.0" ?>
<theresponse>
<status>OK</status>
<pricing currency="USD" symbol="$">
<price class="items">24.00</price>
<price class="shipping">6.00</price>
<price class="tax">1.57</price>
</pricing>
</theresponse>
I know the basic of parsing delegate methods, I just want to know what the code would look like in didEndElement/foundCharacters/didStartElement for retreiving above items(currency/items/shipping/tax)? any help greatly appreciated.
This is a little more tricky than some standard NSXMLParser code; Because essentially when you are looking for the "shipping" you want "6.00" but those two pieces of data are returned to you in different delegate methods, which is normal. But usually the element would be named "shipping" so in parser:didEndElement:namespaceURI:qualifiedName: you would automatically have the element name as it was passed into the method.
The solution would seem simple, have a _currentAttributes ivar and in parser:didStartElement:namespaceURI:qualifiedName:attributes: do something like _currentAttributes = attributeDict; and then handle this in the didEndElement: method. However this style would easily break, even on this moderately simple XML.
My way of handling this would be to store the attributes dictionary passed into the didStartElement: and set it in a dictionary as the object for the key of the element name. Combining this style with the standard use of an NSMutableString as a characterBuffer of sorts allows you to put all of your logic into the didEndElement: method.
Side note: I am also quite fond of having my NSXMLParserDelegate classes be NSXMLParser subclasses, as this one is. However the delegate methods would be identical if it were not.
ItemParser.h
#import <Foundation/Foundation.h>
#interface ItemParser : NSXMLParser <NSXMLParserDelegate>
#property (readonly) NSDictionary *itemData;
#end
ItemParser.m
#import "ItemParser.h"
#implementation ItemParser {
NSMutableDictionary *_itemData;
NSMutableDictionary *_attributesByElement;
NSMutableString *_elementString;
}
-(NSDictionary *)itemData{
return [_itemData copy];
}
-(void)parserDidStartDocument:(NSXMLParser *)parser{
_itemData = [[NSMutableDictionary alloc] init];
_attributesByElement = [[NSMutableDictionary alloc] init];
_elementString = [[NSMutableString alloc] init];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
// Save the attributes for later.
if (attributeDict) [_attributesByElement setObject:attributeDict forKey:elementName];
// Make sure the elementString is blank and ready to find characters
[_elementString setString:#""];
}
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string{
// Save foundCharacters for later
[_elementString appendString:string];
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName{
if ([elementName isEqualToString:#"status"]){
// Element status only contains a string i.e. "OK"
// Simply set a copy of the element value string in the itemData dictionary
[_itemData setObject:[_elementString copy] forKey:elementName];
} else if ([elementName isEqualToString:#"pricing"]) {
// Pricing has an interesting attributes dictionary
// So copy the entries to the item data
NSDictionary *attributes = [_attributesByElement objectForKey:#"pricing"];
[_itemData addEntriesFromDictionary:attributes];
} else if ([elementName isEqualToString:#"price"]) {
// The element price occurs multiple times.
// The meaningful designation occurs in the "class" attribute.
NSString *class = [[_attributesByElement objectForKey:elementName] objectForKey:#"class"];
if (class) [_itemData setObject:[_elementString copy] forKey:class];
}
[_attributesByElement removeObjectForKey:elementName];
[_elementString setString:#""];
}
-(void)parserDidEndDocument:(NSXMLParser *)parser{
_attributesByElement = nil;
_elementString = nil;
}
-(void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError{
NSLog(#"%# with error %#",NSStringFromSelector(_cmd),parseError.localizedDescription);
}
-(BOOL)parse{
self.delegate = self;
return [super parse];
}
#end
And so to test I stored the XML you posted above into a file named "ItemXML.xml". And tested it using this code:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"ItemXML" withExtension:#"xml"];
ItemParser *parser = [[ItemParser alloc] initWithContentsOfURL:url];
[parser parse];
NSLog(#"%#",parser.itemData);
The result I got was:
{
currency = USD;
items = "24.00";
shipping = "6.00";
status = OK;
symbol = "$";
tax = "1.57";
}

how to retain values in the following method?

#interface SignDocumentController : UIViewController<NSXMLParserDelegate> {
NSMutableString *signFaxString;
NSString * messageId;
NSMutableData *xmlData;
NSURLConnection *connectionInprogress;
NSURLConnection *connectionInprogress2;
NSString * annotationKey;
NSString *firstName;
NSString *lastName;
NSString *date;
NSString *signature;
IBOutlet UIImageView *image;
}
#property(nonatomic,retain)UIImageView * image;
#end
-(void)parser:(NSXMLParser *)parser
didStartElement:(NSString *) elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqual:#"SignatureInfo"]) {
signFaxString = [[NSMutableString alloc]init];
firstName = [attributeDict objectForKey:#"FirstName"];
lastName = [attributeDict objectForKey:#"LastName"];
date = [attributeDict objectForKey:#"Date"];
signature = [attributeDict objectForKey:#"Signature"];
}
if ([elementName isEqual:#"AddAnnotationResult"]) {
signFaxString = [[NSMutableString alloc]init];
}
}
the values for firstName, lastName, date, signature do not stay and I get an error when I try accessing firstName, lastName ETC in a different method:
[CFString respondsToSelector:]: message sent to deallocated instance 0x4ec63b0
I have tried using :
firstName = [NSString stringWithString attributeDict objectForKey:#"FirstName"];
but that does not work either. I know this is a silly question but I could use some help.
Thanks
you could also declare the firstName and others as property and retain . As below
#property(nonatomic,retain)NSString* firstName;
#property(nonatomic,retain)NSString* lastName;
#property(nonatomic,retain)NSString* date;
#property(nonatomic,retain)NSString* signature;
And in .m class.
#synthesize firstName,date,lastName,signature;
and release them in dealloc function.
Use with self all your property variable in you class.
self.firstName = [NSString stringWithString:attributeDict objectForKey:#"FirstName"];
EDITED:
Also consider #bbum comment ..
To retain it, just send a retain message to the object.
firstName = [[attributeDict objectForKey:#"FirstName"] retain];
release it later.