How to parse an XML using NSXMLParser with different Nodes? - iphone

I know it may look like a duplicate for XML Parsing posts but i am really not able to understand the nodes and the delegate methods on how they behave. I have an XML..
<?xml version="1.0" encoding="UTF-8"?>
<ParticipantService>
<Response>
<CourseProperties>
<CourseTitle>AICC_Flash_Workshop_PPT_to_web_examples</CourseTitle>
<CourseCode>123456</CourseCode>
<Availability>Open</Availability>
<Status>In Progress</Status>
<ImageLink>HTTP://lmsstaging.2xprime.com/images/inprogress_icon.png</ImageLink>
<CategoryCode>0</CategoryCode>
<CategoryDesc>General</CategoryDesc>
</CourseProperties>
<CourseProperties>
<CourseTitle>Behaviours</CourseTitle>
<CourseCode>OBIUS</CourseCode>
<Availability>Open</Availability>
<Status>In Progress</Status>
<ImageLink>HTTP://lmsstaging.2xprime.com/images/inprogress_icon.png</ImageLink>
<CategoryCode>0</CategoryCode>
<CategoryDesc>General</CategoryDesc>
</CourseProperties>
<CourseProperties>
<CourseTitle>Customer Service Skills (Part - one)</CourseTitle>
<CourseCode>css_1</CourseCode>
<Availability>Open</Availability>
<Status>In Progress</Status>
<ImageLink>HTTP://lmsstaging.2xprime.com/images/inprogress_icon.png</ImageLink>
<CategoryCode>0</CategoryCode>
<CategoryDesc>General</CategoryDesc>
</CourseProperties>
....
My requirement is to store the relevant course details into respective array's. so i declared six nsmutablearray's but getting confused on how to retreive the data from the XMl. I am trying it out this way
in foundCharacters method i am appending the value of string as
videoUrlLink = [NSMutableString stringWithString:string];
and in didEndElement method
if ([elementName isEqualToString:#"CourseTitle"]) {
[courseDetailList addObject:string];
}
but at the end of the XMl i am able to store only one Value in the array. Please let me know if i am going wrong somewhere?

I assume you have a class called Course, and a Course object has properties for title, code, availability and so on.
Make an iVar currentCourse.
Then, in your parser:didStartElement:namespaceURI:qualifiedName:attributes: (note: did start, not end!) method:
if ([elementName isEqualToString:#"CourseProperties"]) {
//create a new course object
currentCourse = [[Course alloc] init];
}
This gives context to all the properties of the course that follow. In the didEndElement: method, you basically do this for all the course properties:
if ([elementName isEqualToString:#"CourseTitle"]) {
[currentCourse setTitle:string];
}
And, last but not least, once the CourseProperties closing tag is found, save the new course somewhere (also in didEndElement:):
if ([elementName isEqualToString:#"CourseProperties"]) {
//create a new course object
[allMyCourses addObject:currentCourse];
currentCourse = nil;
}

Related

How to convert JSON to Object

I defined some custom classes, such as Teacher, Student...
Now I receive teacher info (JSON string) from remote server.
How can I convert the JSON string to Teacher object.
In Java, it's easy to implement a common method for all class (Teacher, Student...) with reflect.
But in Objective-C on iOS, the best way I can find is to use Entity of Core Data, which has setValue:forKey method. First I convert the JSON string to NSDictionary, the set the key-value pair in the disctionary to the Entry.
Is there any better ways?
(I'm from China, so maybe my English is poor, sorry!)
First, do you use JSON Parser? (if not, i'd recommend using SBJson).
Second, why not create an initWithDictionary init method in your custom class that returns self object?
These are all good frameworks for JSON parsing to dictionaries or other primitives, but if you're looking to avoid doing a lot of repetitive work, check out http://restkit.org . Specifically, check out https://github.com/RestKit/RestKit/blob/master/Docs/Object%20Mapping.md This is the example on Object mapping where you define mapping for your Teacher class and the json is automagically converted to a Teacher object by using KVC. If you use RestKit's network calls, the process is all transparent and simple, but I already had my network calls in place and what I needed was to convert my json response text to a User object (Teacher in your case) and I finally figured out how. If that's what you need, post a comment and I'll share how to do it with RestKit.
Note: I will assume the json is output using the mapped convention {"teacher": { "id" : 45, "name" : "Teacher McTeacher"}}. If it's not this way, but instead like this {"id" : 45, "name" : "Teacher McTeacher"} then don't worry ... object mapping design doc in the link shows you how to do this...a few extra steps, but not too bad.
This is my callback from ASIHTTPRequest
- (void)requestFinished:(ASIHTTPRequest *)request {
id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType:[request.responseHeaders valueForKey:#"Content-Type"]]; // i'm assuming your response Content-Type is application/json
NSError *error;
NSDictionary *parsedData = [parser objectFromString:apiResponse error:&error];
if (parsedData == nil) {
NSLog(#"ERROR parsing api response with RestKit...%#", error);
return;
}
[RKObjectMapping addDefaultDateFormatterForString:#"yyyy-MM-dd'T'HH:mm:ssZ" inTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]]; // This is handy in case you return dates with different formats that aren't understood by the date parser
RKObjectMappingProvider *provider = [RKObjectMappingProvider new];
// This is the error mapping provider that RestKit understands natively (I copied this verbatim from the RestKit internals ... so just go with it
// This also shows how to map without blocks
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping mapKeyPath:#"" toAttribute:#"errorMessage"];
[provider setMapping:errorMapping forKeyPath:#"error"];
[provider setMapping:errorMapping forKeyPath:#"errors"];
// This shows you how to map with blocks
RKObjectMapping *teacherMapping = [RKObjectMapping mappingForClass:[Teacher class] block:^(RKObjectMapping *mapping) {
[mapping mapKeyPath:#"id" toAttribute:#"objectId"];
[mapping mapKeyPath:#"name" toAttribute:#"name"];
}];
[provider setMapping:teacherMapping forKeyPath:#"teacher"];
RKObjectMapper *mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:provider];
Teacher *teacher = nil;
RKObjectMappingResult *mappingResult = [mapper performMapping];
teacher = [mappingResult asObject];
NSLog(#"Teacher is %# with id %lld and name %#", teacher, teacher.objectId, teacher.name);
}
You can obviously refactor this to make it cleaner, but that now solves all my problems.. no more parsing... just response -> magic -> Object
Specifically, check out https://github.com/fanpyi/jsontooc/blob/master/README.md
This is the example convert JSON data to Objective-C model, use nodejs.

Three20 Passing of Object

i would like to know that after passing object from view to view with URL, how to i pass it to the model
so i can use it for web service and populate the datasource.
Using Three20 (:
Thanks.
Copied from: http://three20.info/article/2010-10-06-URL-Based-Navigation
Original Author: Jeff Verkoeyen
One of the first questions people ask about TTNavigator is how to pass native objects around, rather than encoding them somehow in a URL. There is a simple pattern for this, using the query property of TTURLAction (or its equivalent convenience function, applyQuery:). For example, imagine you wanted to pass along an NSArray of items to show in the new view:
NSArray *arr = [...load up with data...];
[[TTNavigator navigator] openURLAction:[[TTURLAction actionWithURLPath:#"tt://restaurant/Chotchkie's"]
applyQuery:[NSDictionary dictionaryWithObject:arr forKey:#"arrayData"]]];
In this example, the array is passed directly to the initWithName: but only if there is a matching selector that accepts the query:
-(id) initWithName: (NSString*)name query:(NSDictionary*)query {
for (MyObject* item in [query objectForKey:#"arrayData"])
//... do something with item ...
}
// ...
}

copying object from NSMutableArray to another using IF statement

OK, maybe I'm not seeing clear anymore and hope you can help.
I'm trying to select an Object from a NSMutableArray using:
if([car.seat isEqualToString:#"fancyOne"]){
fancyThings = [[NSMUtableArray]init];
[fancyThings addObjects: car];
}
Now I forgot to tell you I'm new at this Objective-C, so maybe I'm thinking the wrong way.
What I'm basically trying to do is to get an Object from one array by selecting a value of it's components.
This is the way to do it, I am however keep having trouble with my if-statement.
If I leave out the IF-statement it does fill my other NSMutableArray with the exact same object (thisCar) but if I put in the IF-statement it doesn't pick up that the string is the same in thisCar.seat.
I next example it puts everything in the normalThings but there are some aCar.seats which contain the string FANCYONE. I checked the XML file on spaces and that sort of things but everything is in order as far as I can see.
Shall I build it using NSScanner instead of IsEqualToString?
- (void)viewDidLoad {
appDelegate = (XMLAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.fancyThings = [[NSMutableArray alloc]init];
for (CARS *aCar in appDelegate.someCars) {
if ([aCar.seats isEqualToString:#"FANCYONE"]){
[appDelegate.fancyThings addObject:aCar];
}
else {
[appDelegate.normalThings addObject:aCar];
}
}
self.title = #"Cars";
super viewDidLoad];
}
EDIT:
My BAD!! The code supplied was in fact in order!
There was a mistake in my XMLParser, which added blank lines to the strings, so I couldn't get an equal string!
Hopefully this will give you some guidance:
//init new array
NSMutableArray *fancyThings = [[NSMutableArray alloc] init];
//walk your array
for (SomeCarObject *thisCar in arrayOfCars) {
//is thisCar a qualifying object
if ([thisCar.seat isEqualToString:#"fancyOne"]) {
//yes, add thisCar object
[fancyThings addObject:thisCar];
}
}
You'll want to create that NSMutableArray outside of the for loop (assuming you're iterating through a collection). Then you can add to that NSMutableArray like you did.
Hope this helps!
BTW, you should edit your question with the comment you made to elaborate on it..
It's depends from volume of objects, which u deal with. If there is 1000 objects or less, this method looks good. But if there is more objects, u have risk to freeze u application and have a big memory leaks.
Also if u will need concurrency code later, u have to keep in u mind some
other solutions.
U can using not just a string objects in u array, u can try to fill u array after application startup in objects, which response if string is same or not. Or using nsdictionary with appropriate keys.
Please read my post multithread search design

Retrieve properties of "id" type object iphone

I'm facing some difficulty in retrieving properties of "id" type object. This is how I'm accessing it:
I'm doing following to assign an object to id type object from a generic array containing different types of objects and calling method "savedata" to which I'm passing the object as well as its type:
for(id objInArray in genericArray){
NSString *objType = [objInArray valueForKey:#"type"];
[objInArray retain];
[self saveData:objInArray :objType];
}
In savedata method I'm writing following code to retrieve the properties of id object:
-(void)saveData:(id)object :(NSString *)objectType
{
self.managedObjectContext = appDelegate.managedObjectContext;
if([objectType isEqualToString:#"event"])
{
Event * newEvent = (Event *)[NSEntityDescription
insertNewObjectForEntityForName:#"Event"
inManagedObjectContext:self.managedObjectContext];
[newEvent setEletitle:[NSString stringWithFormat:#"%#", [object valueForKey:#"eletitle"]]];
[self saveAction];
}
But the object "object" containing the values fails to assign them to object newEvent.
I also tried to retrive this value in a string object like this:
NSString *eletit = [object valueForKey:#"eletitle"];
[eletit retain];
But eletit is also invalid at the end of this transaction.
Can anybody please help? This' really urgent.
Thanx in advance.
I don't have you answer unfortunately but I have few comments on your code.
Are you sure it's normal you array contain so generics object? It's strange because all your object contained in your array need to respond to "type" or "eletitle" messages, so I guess objInArray is less generic than just "id".
Second, it's not recommended to have selector like saveData::, in Objective-C it's usual and recommended to name the arguments, it's more understandable.

How to write a value validation method for core data?

The docs say:
you should implement methods of the
form validate:error:, as defined by the NSKeyValueCoding protocol
so lets say I have an attribute which is an int: friendAge
I want to make sure that any friend may not be younger than 30. So how would I make that validation method?
-validateFriendAge:error:
What am I gonna do in there, exactly? And what shall I do with that NSError I get passed? I think it has an dictionary where I can return a humanly readable string in an arbitrary language (i.e. the one that's used currently), so I can output a reasonable error like: "Friend is not old enough"... how to do that?
You can do anything you want in there. You can validate that the age is between ranges or any other logic you want.
Assuming there is an issue, you populate the error and have at least a value for the NSLocaliedDescriptionKey. That error will then be handed back to the UI or whatever this value is getting set from and allow you to handle the validation error. This means if there is other useful information you may need in the calling/setting method, you can add it into the NSError here and receive it.
For example:
-(BOOL)validateFreindAge:(NSNumber*)ageValue error:(NSError **)outError
{
if ([ageValue integerValue] <= 0) {
NSString *errorDesc = #"Age cannot be below zero.";
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:errorDesc forKey:NSLocalizedDescriptionKey];
*error = [NSError errorWithDomain:#"MyDomain" code:1123 userInfo:dictionary];
return NO;
}
return YES;
}