Tutorials

JSON

JSON wordt gebruikt voor veel APIs, en ik zal daar op ingaan nadat ik algemene informatie over JSON heb gegeven. Daarna zal ik ingaan op hoe je zelf JSON kan genereren, en dat dan ook in de context van OOP. JSON is beschikbaar vanaf PHP 5.2, maar in PHP 5.4 is er een heleboel toegevoegd, dus ik zal het vermelden als het nieuw is.

Pagina 1

JSON syntax

Dit is een voorbeeld van JSON syntax:
[code lang=js file=http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric]
{
"coord":{
"lon":4.88969,
"lat":52.374031
},
"sys":{
"country":"NL",
"sunrise":1377924671,
"sunset":1377973794
},
"weather":[
{
"id":300,
"main":"Drizzle",
"description":"light intensity drizzle",
"icon":"09d"
}
],
"base":"gdps stations",
"main":{
"temp":17.9,
"humidity":82,
"pressure":1026,
"temp_min":17.78,
"temp_max":18
},
"wind":{
"speed":6.17,
"deg":330
},
"rain":{
"3h":0
},
"clouds":{
"all":24
},
"dt":1377937322,
"id":2759794,
"name":"Amsterdam",
"cod":200
}
[/code]
JSON staat voor JavaScript Object Notation, wat al aangeeft dat de syntax van JSON erg op die van JavaScript lijkt. Het is in principe precies hetzelfde als JavaScript behalve dat dubbele quotes verplicht zijn om keys in JSON. Ik zal even alle elementen uitleggen:

value
Alles in JSON is een value:

Dit betekent dat je naast strings, integers, floats, objects en arrays, je ook true, false en null in een value kunt zetten. De meest complexe values zijn deze:

{ ... } (object)
De accolades geven een JSON object aan. In een object kunnen properties zitten. In het voorbeeld hierboven zitten 12 properties van het object. Je kan zien dat sommige van deze properties ook zelf weer objects zijn.

[ ... ] (array)
Deze blokhaken geven een array aan, deze staan in het voorbeeld hierboven bij weather. In dit geval heeft deze heeft één value, namelijk een object. Als er nog een value in zat, zou het er zo uitzien (ik heb wat weggelaten):
[code lang=js file=http://api.openweathermap.org/data/2.5/forecast?q=Amsterdam,NL&units=metric]
{
"weather":[
{
"id":500,
"main":"Rain",
"description":"light rain",
"icon":"10d"
},
{
"id":506,
"main":"Sun",
"description":"sunny",
"icon":"12d"
}
]
}
[/code]
In een array kunnen als children ook andere values zitten, en wel op deze manier:
[code lang=js]
[
{
"title": "My Blog Post",
...
"tags": ["news", "js"]
}
]
[/code]

Comments zijn niet mogelijk in JSON. Dit heeft een duidelijke reden: JSON is een datastructureringstaal, en deze zou zichzelf moeten beschrijven.
Pagina 2

Toepassingen van JSON

JSON wordt voor heel veel APIs (Application Programming Interface) gebruikt, bij sommige APIs zelfs als enige. Anderen hebben er meestal nog XML naast. Ik zal een voorbeeld noemen.

Twitter
Twitter is een voorbeeld van een API die alleen maar JSON aanbiedt in hun nieuwste versie van de API. Dit is een voorbeeld van een gedetailleerde tweet:
[code lang=js file=https://api.twitter.com/1.1/statuses/show.json?id=210462857140252672]
{
"coordinates":null,
"favorited":false,
"truncated":false,
"created_at":"Wed Jun 06 20:07:10 +0000 2012",
"id_str":"210462857140252672",
"entities":{
"urls":[
{
"expanded_url":"https://dev.twitter.com/terms/display-guidelines",
"url":"https://t.co/Ed4omjYs",
"indices":[
76,
97
],
"display_url":"dev.twitter.com/terms/display-\u2026"
}
],
"hashtags":[
{
"text":"Twitterbird",
"indices":[
19,
31
]
}
],
"user_mentions":[

]
},
"in_reply_to_user_id_str":null,
"contributors":[
14927800
],
"text":"Along with our new #Twitterbird, we've also updated our Display Guidelines: https://t.co/Ed4omjYs ^JC",
"retweet_count":66,
"in_reply_to_status_id_str":null,
"id":210462857140252672,
"geo":null,
"retweeted":true,
"possibly_sensitive":false,
"in_reply_to_user_id":null,
"place":null,
"user":{
"profile_sidebar_fill_color":"DDEEF6",
"profile_sidebar_border_color":"C0DEED",
"profile_background_tile":false,
"name":"Twitter API",
"profile_image_url":"http://a0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png",
"created_at":"Wed May 23 06:01:13 +0000 2007",
"location":"San Francisco, CA",
"follow_request_sent":false,
"profile_link_color":"0084B4",
"is_translator":false,
"id_str":"6253282",
"entities":{
"url":{
"urls":[
{
"expanded_url":null,
"url":"http://dev.twitter.com",
"indices":[
0,
22
]
}
]
},
"description":{
"urls":[

]
}
},
"default_profile":true,
"contributors_enabled":true,
"favourites_count":24,
"url":"http://dev.twitter.com",
"profile_image_url_https":"https://si0.twimg.com/profile_images/2284174872/7df3h38zabcvjylnyfe3_normal.png",
"utc_offset":-28800,
"id":6253282,
"profile_use_background_image":true,
"listed_count":10774,
"profile_text_color":"333333",
"lang":"en",
"followers_count":1212963,
"protected":false,
"notifications":null,
"profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png",
"profile_background_color":"C0DEED",
"verified":true,
"geo_enabled":true,
"time_zone":"Pacific Time (US & Canada)",
"description":"The Real Twitter API. I tweet about API changes, service issues and happily answer questions about Twitter and our API. Don't get an answer? It's on my website.",
"default_profile_image":false,
"profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png",
"statuses_count":3333,
"friends_count":31,
"following":true,
"show_all_inline_media":false,
"screen_name":"twitterapi"
},
"in_reply_to_screen_name":null,
"source":"web",
"in_reply_to_status_id":null
}
[/code]
Je ziet dat er heel veel informatie in de response staat, en ik laat het als een uitdaging voor jou om uit te vogelen wat alles is :).

Composer
Naast dat het gebruikt wordt voor APIs, wordt het ook gebruikt voor configuratiefiles, onder andere door de bekendste PHP package manager, Composer (dit is de composer.json van het Symfony framework):
[code lang=js file=https://github.com/symfony/symfony/blob/master/composer.json]
{
"name": "symfony/symfony",
"type": "library",
"description": "The Symfony PHP framework",
"keywords": ["framework"],
"homepage": "http://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "[email protected]"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"require": {
"php": ">=5.3.3",
"symfony/icu": "~1.0",
"doctrine/common": "~2.2",
"twig/twig": "~1.11",
"psr/log": "~1.0"
},
"replace": {
"symfony/browser-kit": "self.version",
"symfony/class-loader": "self.version",
"symfony/config": "self.version",
"symfony/console": "self.version",
"symfony/css-selector": "self.version",
"symfony/dependency-injection": "self.version",
"symfony/debug": "self.version",
"symfony/doctrine-bridge": "self.version",
"symfony/dom-crawler": "self.version",
"symfony/event-dispatcher": "self.version",
"symfony/filesystem": "self.version",
"symfony/finder": "self.version",
"symfony/form": "self.version",
"symfony/framework-bundle": "self.version",
"symfony/http-foundation": "self.version",
"symfony/http-kernel": "self.version",
"symfony/intl": "self.version",
"symfony/locale": "self.version",
"symfony/monolog-bridge": "self.version",
"symfony/options-resolver": "self.version",
"symfony/process": "self.version",
"symfony/propel1-bridge": "self.version",
"symfony/property-access": "self.version",
"symfony/proxy-manager-bridge": "self.version",
"symfony/routing": "self.version",
"symfony/security": "self.version",
"symfony/security-bundle": "self.version",
"symfony/serializer": "self.version",
"symfony/stopwatch": "self.version",
"symfony/swiftmailer-bridge": "self.version",
"symfony/templating": "self.version",
"symfony/translation": "self.version",
"symfony/twig-bridge": "self.version",
"symfony/twig-bundle": "self.version",
"symfony/validator": "self.version",
"symfony/web-profiler-bundle": "self.version",
"symfony/yaml": "self.version"
},
"require-dev": {
"doctrine/data-fixtures": "1.0.*",
"doctrine/dbal": "~2.2",
"doctrine/orm": "~2.2,>=2.2.3",
"monolog/monolog": "~1.3",
"propel/propel1": "1.6.*",
"ircmaxell/password-compat": "1.0.*",
"ocramius/proxy-manager": ">=0.3.1,<0.5-dev"
},
"autoload": {
"psr-0": { "Symfony\\": "src/" },
"classmap": [
"src/Symfony/Component/HttpFoundation/Resources/stubs",
"src/Symfony/Component/Intl/Resources/stubs"
],
"files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
}
}
}
[/code]

Databaseconfiguratie
Sommigen gebruiken het ook als databaseconfiguratie, of als andere configuratie. Het is wat dat betreft te vergelijken met YAML. Het zou ook wel te vergelijken kunnen zijn met XML, maar XML is veel uitgebreider en heeft meer mogelijkheden.
Pagina 3

JSON parsen

Nu gaat het echte werk beginnen. We gaan JSON parsen met PHP. Om JSON later goed te kunnen bekijken raad ik Postman - REST client of JSONView aan (beiden voor Google Chrome).

In PHP kan je json_decode gebruiken om JSON te parsen. Eerst een simpel voorbeeld:
[code lang=php]
<?php
// we maken eerst een variabele met een JSON object erin
$json = '{"name":"Koen"}';
// dan parsen (decoden) we deze
$decoded = json_decode($json);
// om te bekijken wat er in deze variable zit kan je de volgende regel uncommenten
// var_dump($decoded);

// we echo'en nu de naam die in de variable zit
echo 'De naam is ', $decoded->name;
?>
[/code]
Als je al een beetje verstand hebt van OOP kan je zien dat de JSON data in een object zit (van het standaardtype stdClass). Dit betekent dus dat alle properties te benaderen zijn met de object operator (->). Wil je niet met de object operator werken? Zet dan de tweede parameter van json_decode op true:
[code lang=php]
<?php
// we maken eerst een variabele met een JSON object erin
$json = '{"name":"Koen"}';
// dan parsen (decoden) we deze
$decoded = json_decode($json, true);
// om te bekijken wat er in deze variable zit kan je de volgende regel uncommenten
// var_dump($decoded);

// we echo'en nu de naam die in de variable zit, maar nu d.m.v. een array
echo 'De naam is ', $decoded['name'];
?>
[/code]

Ik zal nog een voorbeeld laten zien, maar dan met arrays.
[code lang=php]
<?php
// we maken eerst een variabele met een JSON array erin
$json = '["Koen","Bas","Wouter","Ger"]';
// dan parsen (decoden) we deze
$decoded = json_decode($json);
// om te bekijken wat er in deze variable zit kan je de volgende regel uncommenten
// var_dump($decoded);

// je kan de values nu benaderen met de normale array accessor, bijvoorbeeld $decoded[0]

// echo alle namen
foreach ($decoded as $name) {
echo 'Een naam is ', $name, '<br />';
}
?>
[/code]
Je kan zien dat je met gedecode JSON arrays alles kan doen wat je met normale arrays kan doen, omdat het een normale array is.
Pagina 4

Wat is het weer?

Ik ga voor dit voorbeeld een simpele pagina met het weer erop maken. Hiervoor zal ik de OpenWeatherMap API gebruiken, omdat deze redelijk simpel is en je niet moeilijk hoeft te doen met access tokens.

De JSON data ophalen
We beginnen met het ophalen van de data met file_get_contents. Op sommige hosts is file_get_contents geblokkeerd, en zal je dus moeten werken met curl. Ik ga daar nu niet op in, daar zijn genoeg tutorials over te vinden. De URL die ik zal gebruiken is http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric. Hiermee halen we het huidige weer in Amsterdam op in graden Celcius. Let's begin:
[code lang=php]
<?php
// haal het huidige weer in Amsterdam op in graden Celcius
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric');
?>
[/code]
Dat was simpel, toch?

De JSON data parsen
Om te zorgen dat de data bruikbaar is, zullen we het parsen met json_decode, dat zal ook niet zo moeilijk zijn.
[code lang=php]
<?php
// haal het huidige weer in Amsterdam op in graden Celcius
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric');

// parse de JSON
$data = json_decode($json);
?>
[/code]

De data weergeven
We zullen nu de data weergeven:
[code lang=php]
<?php
// haal het huidige weer in Amsterdam op in graden Celcius in het Nederlands
$json = file_get_contents('http://api.openweathermap.org/data/2.5/weather?q=Amsterdam,NL&units=metric&lang=nl');

// parse de JSON
$data = json_decode($json);

// laat de stad zien (en het land)
echo '<h1>', $data->name, ' (', $data->sys->country, ')</h1>';

// de algemene informatie over het weer
echo '<h2>Temperatuur</h2>';
echo '<p><strong>Huidig:</strong> ', $data->main->temp, '&deg; C</p>';
echo '<p><strong>Minimum:</strong> ', $data->main->temp_min, '&deg; C</p>';
echo '<p><strong>Maximum:</strong> ', $data->main->temp_max, '&deg; C</p>';

// iets over de lucht
echo '<h2>Lucht</h2>';
echo '<p><strong>Vochtigheid:</strong> ', $data->main->humidity, '%</p>';
echo '<p><strong>Druk:</strong> ', $data->main->pressure, ' hPa</p>';

// en wat informatie over de wind
echo '<h2>Wind</h2>';
echo '<p><strong>Snelheid:</strong> ', $data->wind->speed, ' m/s</p>';
echo '<p><strong>Richting:</strong> ', $data->wind->deg, '&deg;</p>';

// en wat het weer is volgens de API (een array)
echo '<h2>Het weer</h2>';
echo '<ul>';
foreach ($data->weather as $weather) {
echo '<li>', $weather->description, '</li>';
}
echo '<ul>';
?>
[/code]
Je kan dit voorbeeld zelf nog uitbreiden met voorspellingen via de URL http://api.openweathermap.org/data/2.5/forecast?q=Amsterdam,NL&units=metric&lang=nl, maar wij gaan nu kijken hoe we dit zelf kunnen genereren.
Pagina 5

JSON genereren

Je kan zelf ook JSON genereren met json_encode. Je kan objecten genereren door middel van een associative array, dus op de volgende manier:
[code lang=php]
<?php
// zorg ervoor dat het content type goed staat zodat we het mooi kunnen zien met de Chrome extensies :)
header('Content-Type: application/json');

// maak de data aan
$data = array(
'name' => 'Koen'
);

// en genereer de JSON
echo json_encode($data);
?>
[/code]
Je ziet dat er een JSON object uitkomt omdat we een associative array hebben gemaakt. Als je geen extensie hebt geïnstalleerd om JSON 'pretty' te zien, dan zou ik dat nu doen (2 hoofdstukken terug) of je kan als tweede parameter voor json_encode JSON_PRETTY_PRINT (PHP 5.4!) meegeven:
[code lang=php]
<?php
// zorg ervoor dat het content type goed staat zodat we het mooi kunnen zien met de Chrome extensies :)
header('Content-Type: application/json');

// maak de data aan
$data = array(
'name' => 'Koen'
);

// en genereer de JSON
echo json_encode($data, JSON_PRETTY_PRINT);
?>
[/code]

Arrays
Je kan ook JSON arrays genereren, en wel door je array niet associative te maken.
[code lang=php]
<?php
// zorg ervoor dat het content type goed staat zodat we het mooi kunnen zien met de Chrome extensies :)
header('Content-Type: application/json');

// maak de data aan
$data = array(
'Koen',
'Bas',
'Wouter',
'Ger'
);

// en genereer de JSON
echo json_encode($data);
?>
[/code]

Hiermee kan je al een heleboel, en dat zijn gewoon standaard array vaardigheden. In het volgende hoofdstuk zal ik de mogelijkheden voor OOP bespreken.
Pagina 6

OOP?

Als je een API wilt maken, zal je misschien wel een grote website hebben. Ik raad je aan om grote websites volledig OOP te maken, maar hou ga je dan een API maken? Er zijn een paar mogelijkheden:

JsonSerializable
Deze interface is beschikbaar sinds PHP 5.4. Als je deze implement, wordt die automatisch aangesproken als je json_encode aanroept. Een voorbeeld zegt meer dan 1000 woorden:

<?php
class User implements JsonSerializable {
    private $name;
    private $age;

    public function __construct($name, $age) {
        $this->name = $name;
        $this->age  = $age;
    }

    public function getName() {
        return $this->name;
    }

    public function setName($name) {
        $this->name = $name;
    
        return $this;
    }

    public function getAge() {
        return $this->age;
    }

    public function setAge($age) {
        $this->age = $age;
    
        return $this;
    }

    public function jsonSerialize() {
        return array(
            'name' => $this->name,
            'age'  => $this->age
        );
    }

}

header('Content-Type: application/json');
$user = new User('Koen', 15);
echo json_encode($user);
?>


Geen PHP 5.4?
Als je geen PHP 5.4 hebt, kan je JsonSerializable helaas niet gebruiken. Er zijn andere vergelijkbare oplossingen die wel beschikbaar zijn, zoals het Symfony Serializer component. De Symfony Serializer is maar een klein component. Heb je meer nodig? Gebruik dan de JMSSerializer.
Pagina 7

En verder?

Ik hoop dat je nu de JSON syntax begrijpt, en je al een hele voorspelling hebt gemaakt van het weer :). Je kan nu ook werken met de Twitter, Facebook en Foursquare API, dus begin maar aan je nieuwe website met integratie van Twitter :).

Ik zou voorstellen om een API te maken van een bestaande website (die je zelf hebt gemaakt uiteraard) en daarmee je skills te verbeteren. Heb je geen website waarvoor je een API kan maken? Maak dan een website waarvoor je wel een API zou kunnen maken. Je zou zelfs naast een RSS feed van je blog een JSON feed (bestaat niet hoor :)) kunnen maken om te oefenen.

Reacties

0
Nog geen reacties.