POSTing JSON To A Web Service With PHP

I needed to POST JSON formatted data to a RESTful web service using PHP earlier, so I thought I’d share my solution.

There are a couple of approaches that could be taken, using the CURL extension, or file_get_contents and a http context. I took the later way.

When POSTing JSON to a RESTful web service we don’t send post fields or pretend to be a form, instead we have to send the JSON data in body of the request, and let the web service know it’s JSON data by setting the Content-type header to application/json.

$article = new stdClass();
$article->title = "An example article";
$article->summary = "An example of posting JSON encoded data to a web service";

$json_data = json_encode($article);

$post = file_get_contents('http://localhost/rest/articles',null,stream_context_create(array(
    'http' => array(
        'protocol_version' => 1.1,
        'user_agent'       => 'PHPExample',
        'method'           => 'POST',
        'header'           => "Content-type: application/json\r\n".
                              "Connection: close\r\n" .
                              "Content-length: " . strlen($json_data) . "\r\n",
        'content'          => $json_data,
    ),
)));

if ($post) {
    echo $post;
} else {
    echo "POST failed";
}

Here I’m first creating an example PHP stdClass object, populating it with some data, then serialising it to a JSON string. The real magic is using file_get_contents to POST it over HTTP to my test web service. If the POST succeeds, then it’s displayed, else an error message is shown.

It’s important to note I send the header, Connection: close . Without this, your script will hang until the web server closes the connection. If we include it, then as soon as the data has POSTed the connection is closed and control returned to the script.

Parsing JSON Boolean Values With Perl

A project I’ve been working on recently has meant me using both the JSON and JSON::Streaming::Reader Perl modules.

The JSON::Streaming::Reader is great for processing large files, and produces results that are compatible with the JSON module. Compatible… well almost. They both handle booleans in a slightly different way, and that caused me a few problems as I was mainly testing against the JSON module.

In JSON::Streaming::Reader boolean values become references to either 1 or 0.

The JSON module returns a JSON::Boolean (or if using XS – JSON::XS::Boolean) object that is either JSON::true, or JSON::False. These are overloaded to act as 1 or 0.

If you look at a Data::Dumper output, here are how they compare for a “true” value.

JSON::Streaming::Reader

$VAR1 = {
'OnSale' = 1
};

JSON

$VAR1 = {
'OnSale' => bless( do{(my $o = 1)}, 'JSON::XS::Boolean' )
};

For “false” the 1’s become 0’s.

When using the JSON module, because the JSON::Boolean object is overloaded, you can test for truthfullness by simply doing…

if ($decodedjson->{'OnSale'}) {
## This is true.
}

However using JSON::Streaming::Reader this won’t work, as the reference will always evaluate to true. In this case the value must be dereferenced first before testing.

if (${$decodedjson->{'OnSale'}}) {
## This is true.
}

The good news is that this same block of code will work when also using the JSON module, so in future, when testing decoded boolean values from JSON data, always dereference first!

Serialising A JavaScript Object To JSON

As part of my Draw Something Solver I needed to convert a JavaScript object to a string of JSON data. In the past I’d have resorted to a bit of library code, but as I am coding to a recent version of Safari, I thought I’d try to inbuilt JSON functions available to a modern browser.

The method I needed is JSON.stringify(), and it’s very easy to use.

var text = JSON.stringify(myObject);

The above example serialises myObject into text.

There is an equivilant method, JSON.parse() that can be used to safely take a text JSON string and turn it into an object without relying on JavaScript’s eval() function.

There is a useful chart showing JSON support in modern browsers over at caniuse.com.