How to set up NSDictionary to be used in Restkit POST request - ios

I have an iOS app that is using RestKit and CoreData. I have run into a road block with making a POST request because the request body is formed from a NSDictionary. The problem I have is the request body needs to have duplicate keys. NSDictionary requires unique keys so I'm not sure how to make this work.
Here is how the server is expecting the request body.
<node>
<personId>2</personId>
<status>2</status>
<title>Dinosaur unearthed somewhere out there. </title>
<point>
<city>Somewhere</city>
<copy><![CDATA[An amazing discovery was unearthed as a local farmer was plowing his field]]></copy>
<state>Outthere</state>
<sequence>1</sequence>
</point>
<point>
<city>Somwhere</city>
<copy><![CDATA[Archeologists from around the world are amazed at the pristine condition of the remains. Also of note is that the farmer was only using a single blade plow on his John Deere tractor when it was unearthed. ]]></copy>
<sequence>2</sequence>
<state>Outthere</state>
</point>
<point>
......
</point>
</node>
This is a simplified version of how I tried to make it work....
params = [[NSMutableDictionary alloc] init];
nodes = [[NSMutableDictionary alloc] init];
nodeParams = [[NSMutableDictionary alloc] init];
NSString *personId = #"2";
NSString *status = #"2";
NSString *title = #"Dinosaur unearthed somewhere out there.";
NSString *city = #"Somewhere";
NSString *state = #"OutThere";
NSString *copyField = #"Testing this out to see if it works";
//Here I set up the point layer of the request body
//In my code this three line section is in a loop. Obviously this does not work because it just overwrites the objectForKey each time through the loop.
[params setObject:copyField forKey:#"copy"];
[params setObject:city forKey:#"city"];
[params setObject:state forKey:#"state"];
//Here I Set up the Node Layer of the request body
[nodeParams setObject:params forKey:#"point"];
[nodeParams setObject:personId forKey:#"personId"];
[nodeParams setObject:status forKey:#"status"];
[nodeParams setObject:title forKey:#"title"];
[nodes setObject:nodeParams forKey:#"node"];
NSLog(#"The Dictionary is %#",nodes);
At runtime the log shows that the body is formatted properly except their is only one point layer and it is populated with the data from the final pass in the loop. Does anyone know of any trickery to get around this?
As a note I believe the postObject method requires the NSDictionary because it passes the dictionary to a JSON Serialization tool. I suspect the serialization tool is expecting a dictionary to be passed in. If someone knows otherwise correct me if I'm wrong.

Okay I finally figured this out. It wasn't until I converted the XML above into JSON that I realized how it could be done. The key is recognizing how an Array and NSDictionary are formatted in the 'NSJSONSerialization' class.
Here is an example of how the server was expecting the JSON.
{ "status":"2",
"title":"test #5",
"personId":"1",
"point":[
{ "copy":"<p class=\"pt\" data-seq=\"1\">This is paragraph #1<\/p>",
"state":"OutThere",
"city":"Somewhere",
"sequence":"1"
},
{ "copy":"<p class=\"pt\" data-seq=\"2\">This is paragraph #2<\/p>",
"state":"OutThere",
"city":"Somewhere",
"sequence":"2"
}
]
}
The critical thing to note here is that the "point" field is and Array of Dictionaries. The [ denotes an array. Once I finally clued in on that I realized that each time through the loop I could reinitialize my dictionary, add the new objects, and then add the current dictionary to the array as you can see below.
while (![theScanner isAtEnd]) {
count = count +1;
//Initialize the dictionary
points = [[NSMutableDictionary alloc] init];
NSString *htmlTag = [NSString stringWithFormat:#"<p class=\"pt\" data-seq=\"%d\">",count];
[theScanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] intoString:&temp];
NSString *preTagText = [htmlTag stringByAppendingString:temp];
NSString *postTagText = [preTagText stringByAppendingString:#"</p>"];
NSString *sequence = [NSString stringWithFormat:#"%d",count];
[points setObject:postTagText forKey:#"copy"];
[points setObject:sequence forKey:#"sequence"];
[points setObject:city forKey:#"city"];
[points setObject:state forKey:#"state"];
[pointArray addObject:points];
points = nil;
}
[params setObject:titleText forKey:#"title"];
[params setObject:personIdNumber forKey:#"personId"];
[params setObject:status forKey:#"status"];
[params setObject:pointArray forKey:#"point"];
After the loop is finished I just add the pointArray to the dictionary as the object with the key point (Thanks Wain). I add the title, status, and personId objects to the same dictionary and use it as the NSDictionary required for the Restkit POST request.

Related

Unable to retrieve the data from Dictionary

In my project I am getting response from the server in the form
response:
<JKArray 0x7fa2e09036b0>(
{
id = 23;
name = "Name1";
},
{
id = 24;
name = "Name2";
}
)
From this response array i am retrieving the objects at different indexes and then adding them in a mutableArray and then into a contactsDictionary.
self.contactsDictionary = [[NSMutableDictionary alloc] init];
for(int i=0 ; i < [response count] ; i++)
{
NSMutableArray *mutableArray=[[NSMutableArray alloc] init];
[mutableArray addObject:[response objectAtIndex:i]];
[self.contactsDictionary setObject:mutableArray forKey:[NSString stringWithFormat:#"%i",i]];
}
I want to retrieve data for Key #"name" from the contactsDictionary at some other location in the project. So how to do it.
Thanks in advance....
this is the wrong way like you are setting your contactsDictionary.
replace below line
[self.contactsDictionary setObject:mutableArray forKey:[NSString stringWithFormat:#"%i",i]];
with
[self.contactsDictionary setObject:[mutableArray objectAtIndex :i] forKey:[NSString stringWithFormat:#"%i",i]];
becuase everytime your array have new objects so your contacts dictionary's first value have one object then second value have two object. so you shouldn't do that.
now, if you want to retrieve name then call like
NSString *name = [[self.contactsDictionary objectForKey : #"1"]valueForKey : #"name"];
avoid syntax mistake if any because have typed ans here.
Update as per comment:
just take one mutablearray for exa,
NSMutableArray *arr = [[NSMutableArray alloc]init];
[arr addObject : name]; //add name string like this
hope this will help :)
Aloha from your respond I can give you answer Belo like that according to you response.
for(int i=0;i<[arrRes count];i++);
{
NSString *strId = [NSString stringWithFormat:#"%#",[[arrRes obectAtIndex:i]objectForKey:#"id"]];
NSString *StrName = [NSString stringWithFormat:#"%#",[[arrRes objectAtIndex:i]objectForKey:#"name"]];
NSLog(#"The ID is -%#",strId);
NSLog(#"The NAME is - %#",strName);
}

string handling in ios

I am getting String data from server in the format below. I want to get the value for every tag like phonenumber and name etc. I am able to convert it in array by comma separator. how to get individual values?
Company:Affiliated CO,Organization:TECHNICAL EDUCATION
SOCIETY,Organization:SINHGAD,Organization:National Basketball Association,Person:Parikshit N. Mahalle,PhoneNumber:81 98 22 416 316,PhoneNumber:9120-24100154,Position:Professor,SportsEvent:NBA.
Say your original string is stored in rawString.
You need to :
1) split the string by ,
NSArray *pieces = [rawString componentsSeparatedByString:#","];
2) for each item in this array, split it by :, and add it to a dictionary :
NSMutableDictionary *dict = [NSMutableDictionary new];
for (NSString *piece in pieces) {
NSArray *splitPiece = [piece componentsSeparatedByString:#":"];
// key is at splitPiece[0], value is at splitPiece[1]
dict[splitPiece[0]] = splitPiece[1];
}
Then you'll have a dictionary of what you wanted in the first place.
But as suggested in the comments, it would be far better (and more flexible) for you to receive JSON data.
Edit: your original string shows there are multiple fields named Organization. The code I've given is not designed to handle such cases, it's up to you to build upon it.
If this data is not being returned as a JSON object then you'll have to go with #Clyrille answer. But if it is JSON then NSJSONSerialization:JSONObjectWithData:options:error: will be the way to go.
EXAMPLE
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:/*urlResponse*/ options:0 error:nil];
NSString *company = [json objectForKey:#"Company"];
NSString *Organization = [json objectForKey:#"Organization"];
NSString *Person = [json objectForKey:#"Person"];
NSString *PhoneNumber = [json objectForKey:#"PhoneNumber"];
NSString *Position = [json objectForKey:#"Position"];
NSString *SportsEvent = [json objectForKey:#"SportsEvent"];

Previous NSDictionary now to JSON array

- (void)retrieveData
{
NSURL * url = [NSURL URLWithString:#"***/connection.php"];
NSData * data = [NSData dataWithContentsOfURL:url];
_json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
_questionsArray = [[NSMutableArray alloc]init];
for (int i = 0; i < _json.count; i++)
{
NSString * qID = [[_json objectAtIndex:i] objectForKey:#"id"];
NSString * qTitle = [[_json objectAtIndex:i] objectForKey:#"question_title"];
NSString * qA = [[_json objectAtIndex:i] objectForKey:#"A"];
NSString * qB = [[_json objectAtIndex:i] objectForKey:#"B"];
NSString * qC = [[_json objectAtIndex:i] objectForKey:#"C"];
NSString * qD = [[_json objectAtIndex:i] objectForKey:#"D"];
NSString * qAnswer = [[_json objectAtIndex:i] objectForKey:#"question_answer"];
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[_questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
NSLog( #"%#", _questions );
}
Logs (null)
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
random gobledy gook so my post isn't mostly code
If I understand your question correctly it becomes something like this
self.questions = .... //I assume this is the array you reference 'question' objects that is created by your retrieve data method
//this used to be created by pulling an object out of your questions dictionary with the key i interpreted as a string.
//now that it's an array you should be able to just reference it by index, assuming they were inserted in order
//I'm also assuming that what comes out of the aray is a question object given the code you provided with the signature
//- (id) initWithQuestionID: (NSString *) qID andQuestionName: (NSString *) qName andQuestionA: (NSString *) qA andQuestionB: (NSString *) qB andQuestionC: (NSString *) qC andQuestionD: (NSString *) qD andQuestionAnswer: (NSString *) qAnswer
Question *nextQuestion = self.questions[i];
self.answer = nextQuestion.questionAnswer;
self.questionLabel.text = nextQuestion.questionLabel;
//and so on
I also suggest the following edit to replace your for loop. It uses a for in loop instead, this saves you from having to keep track of an index and looks cleaner. It also helps so you don't keep repeating the [_json objectAtIndex:i] chunk of code. I also use modern objective-c syntax to access the dictionary.
for (NSDictionary *questionDictionary in _json)
{
NSString * qID = questionDictionary[#"id"];
NSString * qTitle = questionDictionary[#"question_title"];
...
question * myQuestion = [[question alloc] initWithQuestionID:qID andQuestionName:qTitle andQuestionA:qA andQuestionB:qB andQuestionC:qC andQuestionD:qD andQuestionAnswer:qAnswer];
[_questionsArray addObject:myQuestion];
}
If you need the key along with the object in the dictionary then you can clean it up in a similar way with the enumerateObjectsUsingBlock
[_json enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
//your code here
}];
EDIT
It sounds like what your really wanting to do is to pull down your JSON but keep all your other code the way it was when you were using a dictionary that you got from your plist. So in this case you want your parsing function to return a dictionary instead of an array. If that's the case it's worth sidestepping into computer science for a second.
NSDictionarys are also known as a hash, map, symbol table, or associative array. Some languages (such as Lua) don't have an array collection like NSArray, they only have dictionaries. From a dictionary you can create many of the other collections your used to like arrays (and sets too). Heres how it works: Instead of an ordered collection of elements with an index, you place the items in a dictionary and use what would have been the index as the key, and the value becomes, well, the value. For example an array and it's equivalent associative array (aka dictionary):
NSArray *array = #[#"hello", #"world", #"!"];
NSDictionary *dictionary = #{#(1): #"hello",
#(2): #"world",
#(3): #"!"};
This is exactly what your doing when you load in the data from your plist because the first elements key is 0 followed by another dictionary, and I'm supposing that the next element in the list is 1 followed by another dictionary. Inside your parsing function it becomes
NSMutableDictionary *questions = [[NSMutableDictionary alloc] init];
[_json enumerateObjectsUsingBlock:^(NSDictionary *questionDictionary, NSUInteger idx, BOOL *stop) {
//Here I'm treating the index like an NSNumber, if your code is expecting a string instead use
//#(idx).stringValue
[questions setObject:questionDictionary forKey:#(idx)];
//or the modern equivalent
//questions[#(idx)] = questionDictionary;
//If you want to use your 'questions class' then create one and put it into the array instead of the dictionary pulled from the array.
}];
This of course assumes that your api is going to return the JSON questions in the order you want.

Passing a _CFNSString into an NSString

I have a loop that identifies elements of a webpage via its HTML and extracts the sections I need. I'm wanting to build an array or (very) long string of the extracted text which can be used later.
The extraction uses TFHpple from GitHub. The problem seems to lie with the extracted text being a _CFNSString, and these don't allow me to transpose them into a NSString or NSMutuableArray.
The code I'm using is:
NSArray *webNodes = [webParser searchWithXPathQuery:tutorialsXpathQueryString];
NSString *extractedText = [[NSString alloc] init];
NSMutableArray *extractedArray = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in webNodes) {
Extraction *extraction = [[Extraction alloc] init];
[extractedArray addObject:extraction];
extraction.title = [[element firstChild] content];
extractedText = extraction.title;
NSLog(#"\n\nTitle: %#", extractedText);
}
The NSLog at this point shows me extractedText holds I'm after for each loop, breaking the code shows extractedText to be a _CFNSString.
If I try adding
text = [text StringByAppendingString extractedText];
(with 'text' being an NSString initialised before the loop) as the last step of the loop I get a null value. Its the same if I try adding text or extraction.title directly into an array.
I found this question Convert NSCFString to NSString but the conversion seems to be going the other way (NSString to CFNSString). When I added equivalent code I got bridging errors and the code doesn't run.
How can I collect the data within extraction.title to build a string or array that can be used later?
You said you only want a text.
Get it in one line of code for array:
NSArray *extractedArray = [webNodes valueForKeyPath:#"firstChild.content"];
For string:
NSString *extractedText = [webNodes valueForKeyPath:#"firstChild.content"] componentsJoinedByString:#" "];

Google API returns complex NSDictionary object

When I parse the data of google API for local police station details, it returns an NSDictionary in it with a complex form:
result = (
{
geometry = {
location = {
lat = "22.72187";
lng = "75.8741";
};
};
icon = "http://maps.gstatic.com/mapfiles/place_api/icons/police-71.png";
id = 5bf224eb03bee960670c048f7dcdb2684d6aed1f;
name = "Name Police Station";
reference = "CoQBgAAAACG6clYx-1ycnenczJfECFggwSCVzxqFR8GKwMYpA1QbP1VTRIgHHYNXW7z0kOw9IRuV9gKJ-ES19tf46CcwUShwT_lznIX36sx_F8aKFjYE3APa0zNWRxSGY0fDQ95HwinR9HXhTWeL0ncPHSLo9cL9FB8OlBJF-tYNRP5ZThuMEhCQH0lSxrelWOd";
type = (
police,
establishment
);
vicinity = "Yeshwant Niwas Road, Sanghi Colony";
},
Here, I got many objects of geometry, id, name.
How do I use this data in a simple NSArray and NSDictionary?
NSDictionary *dict = [NSArray>Object objectatIndex:index];
then dict would return value for key like it would return [[dict objectforkey:#"name"];.
Apple documentation helped me to solve above problem:
I use this code to solve above dictionary problem-
NSArray *arryDict = [dictPoliceStatin objectForKey:#"result"];
NSEnumerator *enumerator = [arryDict objectEnumerator];
NSDictionary *anObject;
while (anObject = [enumerator nextObject]) {
/* code to act on each element as it is returned */
NSMutableDictionary *diceGloable = [[NSMutableDictionary alloc] init];
[diceGloable setObject:[[[anObject objectForKey:#"geometry"]objectForKey:#"location"] objectForKey:#"lat"] forKey:#"lat"];
[diceGloable setObject:[[[anObject objectForKey:#"geometry"]objectForKey:#"location"] objectForKey:#"lng"] forKey:#"lng"];
[diceGloable setObject:[anObject objectForKey:#"name"] forKey:#"name"];
[diceGloable setObject:[anObject objectForKey:#"vicinity"] forKey:#"vicinity"];
[arrayMapValues addObject:diceGloable];
}
ObjectEnumerator helped here, arrayMapsValues is the NSMutablearray class object.
Take a look at the FTGooglePlacesAPI library I'm currently working on. This is a complete library for interaction with the Google Places API using simple, block-based Objective-C interface.
There is no readme or how-to at the moment as I don't consider it final/released yet. But it works pretty fine and there is a very detailed, well-commented example project.
You can find the library here: https://github.com/FuerteInternational/FTGooglePlacesAPI
Or you can utilize valueForKeyPath: method of some collections from NSFoundation, which is often overlooked, but very powerful.
Example:
NSDictionary *firstItem = results[0];
NSNumber *lat = [firstItem valueForKeyPath:#"geometry.location.lat"];

Resources