AS3 to Cocoa touch: JSON (Part 2: send JSON)

So for part 2 of my tutorial about json. We are going to use json encoded data to transfer over http from your application to the backend server. There are some things you have to keep in mind if you encode json in cocoa touch using the json framework (which I’ve used in the previous tutorial). But we will go deeper on that subject later on.

So for this part of the tutorial my usercase will be. Send a couple of Person objects over http using json to encode and get an xml in return which lists the Person objects in xml form. This maybe not a practical usercase but it illustrates the point of sending json over http.

So we will use POST for the HTTP Request. Which is a very simple way to send data over http from 1 point to another ( think of all the html forms filled in, in best case they will use POST to send data over from 1 page to another).

Obj-c: data: encode jsonĀ  -> http (post) -> php: decode json + change to xml -> http -> obj-c: XML

So I’m going to provide the php I use, I didn’t wrote it myself, I used google and came with this piece of code.

\n";
    $xml .= "\n";
    foreach($buffer as $val) {
        $xml .= "    \n";
 
        foreach ($val as $key => $value) {
            $xml .= "        &lt;{$key}&gt;".utf8_encode($value)."<!--{$key}-->\n";
        }
 
        $xml .= "    \n";
    }
    $xml .= "\n";
 
    return $xml;
}
$json = $_POST["json"];
$data = json_decode(stripslashes($json));
echo array2xml($data);
?&gt;

So I’m not going to discuss all the code. Just a couple of them.

  • We change our content type to xml
  • We have our function that is going to convert an array into a xml.
  • We have our json data that we get from post (which simply has the name json)
  • We decode our data using json_decode and this is import, since json uses quotation marks in its string we have to use the function stripslashes. Because when we are going to send our json string with Post Http request before every quotation mark it will add a slash. So just make sure to strip those slashes. It will return an array since we send an array to our php.
  • Echo that $data variable using the array2xml which will return an xml String.

So for the actionscript part we have:

package
{
     import com.adobe.serialization.json.JSON;
 
     import flash.display.Sprite;
     import flash.events.Event;
     import flash.net.*;
 
     public class MainJSON extends Sprite
     {
          public function MainJSON()
          {
               var arr : Array = new Array(new Person("Lord Vader",23),new Person("Qui-Gon Jinn",51));
               sendJSON( arr );
          }
          private function sendJSON( a : Array ) : void
          {
               var jsonString : String = JSON.encode(a);
 
               var urlVariables:URLVariables = new URLVariables();
               urlVariables.json = jsonString;
 
               var urlRequest:URLRequest = new URLRequest("http://localhost/iphone/json/array2xml.php");
               urlRequest.method = URLRequestMethod.POST;
               urlRequest.data = urlVariables;
 
               var urlLoader:URLLoader = new URLLoader();
               urlLoader.addEventListener(Event.COMPLETE, onURLLoaderCompleteEvent);
               urlLoader.load(urlRequest);
          }
          private function onURLLoaderCompleteEvent( evt : Event ) : void
          {
               var xml:XML = new XML(evt.target.data);
               trace(xml);
          }
     }
}

Ok, a quick run through.

  • We declare an array with 2 person objects we want to send. And feed it to the sendJSON function.
  • Encode our JSON string using the static encode method of the JSON class from the corelib framework.
  • Declare a URLVariables object which we are going to use to store our post variables in it. Behind the scenes its going to parse a correct httpbody and header message needed to transfer over http.
  • We declare our urlRequest which we tell which url you need to load, what the method will be how we will sending our data , in our case POST, and finally in the data property we put the urlVariables object.
  • We declare our URLLoader which is going to do the request to the page and in our case return an xml
  • When we have an eventListener listening to the URLLoader object on complete you will get some data in return for it when that function is called. We cast it to XML an trace that.

And that’s how we send JSON over http with Actionscript 3.

Now let’s try the same in Cocoa Touch

NSMutableArray *arrayDict = [[NSMutableArray alloc] init];
Person *person1 = [[Person alloc] initWithName:@"Lord Vader" andAge:23];
Person *person2 = [[Person alloc] initWithName:@"Qui-Gon Jinn" andAge:51];
[arrayDict addObject:[person1 getDictionary]];
[arrayDict addObject:[person2 getDictionary]];
[person1 release];
[person2 release];
 
SBJSON *json = [SBJSON alloc];
 
NSString *jsonString = [json stringWithObject:arrayDict error:nil];
[arrayDict release];
[json release];
 
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
 
NSString *post = [NSString stringWithFormat:@"&amp;json=%@",jsonString];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO];
 
[request setURL:[NSURL URLWithString:@"http://localhost/iphone/json/array2xml.php"]];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"content-type"];
[request setHTTPBody:postData];
 
NSURLResponse *urlResponse;
 
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&amp;urlResponse error:nil];
[request release];
NSString *jsonData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
 
NSLog(@"%@",jsonData);
[jsonData release];

So first of all, we do need a project setup with the JSON framework (used in the previous tutorial).

So the first 7 lines of code doesn’t have anything to do with json but its just our data we are going to encode to json and send over to our backend server. But there are a couple of rules we have to keep in mind. And the most important rule is you cannot encode NSObjects to JSON. You have to use NSDictionary instead! When we open the JSON framework documentation ( included in the .dmg file) we can see the conversian table.

Objective-C types are mapped to JSON types and back in the following way:

* NSNull -> Null -> NSNull
* NSString -> String -> NSMutableString
* NSArray -> Array -> NSMutableArray
* NSDictionary -> Object -> NSMutableDictionary
* NSNumber (-initWithBool:) -> Boolean -> NSNumber -initWithBool:
* NSNumber -> Number -> NSDecimalNumber

So In our case we have a value object of the type Person but we cant encode that to JSON. What I did instead was add a method to the Person class that will return an NSDictionary containing the values from the Person objects assigned to the key’s with the exact names as the object property’s.

//Person.h
-(NSDictionary *)getDictionary;
//Person.m
-(NSDictionary *)getDictionary{
return [NSDictionary dictionaryWithObjectsAndKeys:self.name, @"name", [NSNumber numberWithInt:self.age], @"age", nil];
}

Add these lines of code to the Person.h & Person.m class.
What it does is very simple, return an Object of type NSDictionary with the values and properties of the Person class.
(note: that you have to convert the integer age to an Object of type NSNumber).

Now back to our code. We instantiate an object of type NSMutableArray where we are going to keep all our dictionary’s. We make our person Objects andĀ  call the getDictionary method on it and push it into our previously created array.
You can release those objects because we don’t will be needing them anymore now.

Next step in the process will be to encode our array to JSON.
We allocate an object of type SBJSON.
we use the method stringWithObject:error: on it to convert our array to a JSON String, and store it in a NSString object.
We don’t need the array or the json object anymore so we can release that as well.

So now we have our string we want to send to our backend service. We are going to send it the same way like we did in actionscript using HTTP request (POST). It’s a little bit more difficult than in actionscript, and that’s simply because actionscript does a little bit more work under the hood that we have to do for ourselves in objective-C.

So what we need 4 things for our request object

- the url of the backend service (obviously)
- the http method ( POST in our case )
- the http body containing our data
- We need to set the value of 1 http header field ( content-type )

So first we are going to make our http body which is going to hold up our data we want to send.

We format a string with the property’s like this:

&key=value,key=value,..

if we use the function stringWithFormat: we can add directives which we can add as a parameter that are copied directly to the result. But our http body expects an object of type NSData so we need to convert our string to an NSData object which we do by using the method dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:NO on our string. Using the ascii encoding and not allowing it to lossy conversion.

So fill in the right values to the right properties of our request object.

Make a pointer for urlResponse since we are getting an xml back from the backend service. And the next lines of code I already discussed in my previous tutorial about JSON. So I’m not going to run through it again.

So the key principle is to setup your Http Request for POST right, which in objective-C you have to do for yourself.

8 Comments

  1. Raj says:

    Andy fantastic work again. Very very helpful for me :-) JSON is so much lighter to XML, and I think wherever possible, we should be using JSON instead of XML on the iPhone. Even better, a simple CSV file if possible :-) I hope to get some time some day to write a neat String Delimited Framework for iPhone lol

  2. dave says:

    [[NSObject alloc] init] — it’s not just a good idea, it’s the law!

    Every class’s init implementation should call up to the superclass init method. From the docs for -[NSObject init]: “Subclass versions of init need to incorporate the initialization code for the classes they inherit from, through a message to super”. They really shouldn’t have told anyone that NSObject’s init method doesn’t do anything, just returns self–at some point in the future they might add something important to -[NSObject init]. If your code isn’t calling it, things will go very poorly.

    Glad you’re having fun with Cocoa, and I look forward to your iPhone/iPod Touch apps!

  3. Bona says:

    Your blog is interesting! Keep up the good work!

  4. moondy says:

    that have a wrong at : Person?

  5. Zap says:

    Where is the code for Person.h and Person.m? Is there a place i can download the files?

  6. Gareth says:

    Hi,

    Great tutorial, however, unfortunately I’m struggling to get this second part working. I get an error on this line:

    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:nil];

    amp undeclared

    What’s the purpose of this? I haven’t seen it used before.

    I tried removing it but then I get the error:

    Passing arguement 2 of ’sendSychronusRequest:returningResponse:error:’ from incompatible pointer type.

    Please help! I’m not sure what it wants…..

    Thanks in advance.

  7. ahmad imtiaz says:

    thanx for your help buddy.

  8. Daniel K says:

    Tha pointer json needs to be initialized (not only allocated) and the code will work. :)

Leave a Reply