Get JSON data from external website and parse it to show in HTML

Is there a simple example how to fetch JSON data from an external website (like for example http://openweathermap.org/current ) and parse one of the values (e.g. ‘temp’) so it would show up in my HTML?

All i found so far were examples how to generate JSON data in Kirby itself, like here:
https://getkirby.com/docs/cookbook/json

Thanks in advance!

You can use cURL to access the data.

Here’s what I used within a custom plugin. It’s the remote class from the Kirby Toolkit (which also uses cURL btw.).

class myClass {
  public static function getJsonData() {
    $tree = [];
    $url = 'http://api.domain.com';

    $request = remote::request($url);
    
    if (!empty($request->content)) {
      $tree = json_decode($request->content);
    }
    return $tree;
  }

And then …

<?php $tree = myClass::getJsonData() ?>
6 Likes

Ah, the Toolkit, makes our life so much easier!

1 Like

Thanks a lot! I’ll be using this example which i found over here:

http://www.wdrfree.com/110/how-to-weather-with-php-openweathermap-in-3-easy-steps

<?php

//units=For temperature in Celsius use units=metric
//5128638 is new york ID

$url = "http://api.openweathermap.org/data/2.5/weather?id=5128638&lang=en&units=metric&APPID={APIKEY}";


$contents = file_get_contents($url);
$clima=json_decode($contents);

$temp_max=$clima->main->temp_max;
$temp_min=$clima->main->temp_min;
$icon=$clima->weather[0]->icon.".png";
//how get today date time PHP :P
$today = date("F j, Y, g:i a");
$cityname = $clima->name; 

echo $cityname . " - " .$today . "<br>";
echo "Temp Max: " . $temp_max ."&deg;C<br>";
echo "Temp Min: " . $temp_min ."&deg;C<br>";
echo "<img src='http://openweathermap.org/img/w/" . $icon ."'/ >";

?>

I think using cURL/the Toolkit version is a better recommendation than using file_get_contents(). Also, file_get_contents() with an URL object as parameter requires allow_url_fopen to be enabled on your server.

I agree. file_get_contents is a trap for developers like @krl who end up using it without any error checking. If your API goes down it’ll return false and your entire page will break. Either apply correct error handling to this snippet (with a try catch) or use curl/toolkit.

1 Like

Hi Fellaz!

Is f::read the same as file_get_contents?

Saludos, Funkybrotha

  public static function read($file) {
    return @file_get_contents($file);
  }

Yep, but it supresses errors…

This would be save…?

<?php

  $json = f::read('http://samples.openweathermap.org/data/2.5/forecast?id=2657896&appid=b1b15e88fa797225412429c1c50c122a1');
  $test = json_decode($json, true);
  $listItems = ($test['list']);
  //dump($listItems);

?>

<?php foreach ($listItems as $key => $item): ?>
  <div class="temp">
    Temp: <?php echo $item['main']['temp']; ?>
  </div>
<?php endforeach; ?>

At least the f::read() bit wouldn’t break your site. Your complete code is not safe, though, because you assume that you get back what you expect. For example, in case your API key is missing, your $json variable would give you

Array
(
    [cod] => 401
    [message] => Invalid API key. Please see http://openweathermap.org/faq#error401 for more info.
)

If this is the case

 $listItems = ($test['list']);

would throw an error, because you $test variable doesn’t contain an index called list.

Nevertheless, suppressing errors makes your code hard to debug. Not a good idea.

1 Like

Aight! Thanx a lot @texnixe !

OK. As i didn’t know how to implement the plugin solution with the remote class from the Kirby Toolkit i read a bit about cURL and this is my working php snippet now (my API code replaced by 12345). Is this save, or what would i have to do to make it save in case the source of the data is not available?

<?php
$url = 'http://api.openweathermap.org/data/2.5/weather?q=Frankfurt&units=metric&lang=de&APPID=12345';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$json = curl_exec($ch);
if(curl_error($ch)) { 
echo 'error:' . curl_error($ch);
};
curl_close($ch);
$data = json_decode($json,true);
?>
<?php echo $data['main']['temp']; ?> °C /
<?php echo $data['weather'][0]['description'];?>

You’re checking for curl errors, which is good, but you’re still accepting the returned JSON data without verifying it.

If something goes wrong with the request (API down, key expired, place doesn’t exist, etc), $data will look like this:

{
"cod":401, 
"message": "Invalid API key. Please see http://openweathermap.org/faq#error401 for more info."
}

If you try to access $data[‘main’] right now, you’ll throw an error. You should check the returned “cod” value to check if the request was successful before reading values from it.

 <?php $data = json_decode($json,true); ?>
 <?php if($data['cod'] == 200): ?>
    <?= $data['main']['temp']; ?> °C /
    <?= $data['weather'][0]['description'];?>
 <?php else: ?>
    <?= $data['message']; ?> 
 <?php endif; ?>

Even if the returned code is correct, the data you get back may change and the $data index you are expecting doesn’t exist anymore, so you would have to check for that as well.

You don’t necessarily need a plugin with a custom class to use the toolkit stuff. You can go straight forward like this:

<?php
  $data = [];
  $url = 'http://api.openweathermap.org/data/2.5/weather?q=Frankfurt&units=metric&lang=de&APPID=12345';

  $request = remote::request($url);

  if (!empty($request->content)) {
    $data = json_decode($request->content); 
  }
?>

All important curl_setopt options are set by the request() method by default.

Great. After adding true in the json_decode function it works like a charm now.

<?php
$data = [];
$url = 'http://api.openweathermap.org/data/2.5/weather?q=Frankfurt&units=metric&lang=de&APPID=12345';
$request = remote::request($url);
if (!empty($request->content)) {
$data = json_decode($request->content, true);
} ?>
<?php if($data['cod'] == 200): ?>
 <?= round($data['main']['temp']); ?>°C /
 <?= $data['weather'][0]['description'];?>
 <?php else: ?>
<?= $data['message']; ?> 
<?php endif; ?>

Thanks to you all.

1 Like

Just out of curiosity, now this has been solved - is there a way to do something similar, but with an RSS feed as the source rather than JSON? Can the toolkit help with getting the information in an external RSS feed on to the page in a Kirby site?

Yes, you can, together with the XML you should be able to parse it.

Awesome! So I can basically do the same as above, but change the $url variable to the url of an rss feed instead? Obviously i would need to change the rest to match the items in the feed.