Avoiding NullPointException in OpenWeatherMap

Hello,

In theory, a simple question. First of all, apologies if I get my terms wrong as I am new to this…

I am parsing data from OpenWeatherMap. I can get most of it, but the “rain” parameter is more difficult. This is because the OpenWeatherMap api only ‘publishes’ the “rain” parameter whenever it is raining; i.e., if there is no rain, instead of the “rain” parameter just equalling “0”, in fact, the parameter itself ‘disappears’. And this is actually the same for the “snow” parameter.

The problem I am having therefore is that my json address pointing to the “rain” (or “snow”) parameter can’t reach the parameter if it missing (i.e., if it is not raining or snowing), with the result that Processing gives me a “NullPointerException”, and my program stops working.

So, I am thinking there must be a way of writing something like a Boolean If/Else argument, that says something like "if the rain parameter doesn’t exist, then read the “snow” parameter, and if that doesn’t exist either then continue with the next parameter, “WindSpeed”. My problem is that I haven’t worked out how to do this, and so my question is can anyone give me an example of a way of achieving my aim.

What I am looking for is code that locates the data values I have coded for, and if they are missing (for example, if there is no rain) then the program gives the ‘missing’ parameter a value of “0” and proceeds to the next parameter.

Here is the code I am working with at the moment:


import java.util.Date;
Boolean rainy = true;
String url = "https://api.openweathermap.org/data/2.5/weather?";
String APPID = "xxxxxxxxxxxxxxxxxxxxxxxx"; //60cpm - Current(1hr):/weather, 5Day(3hr)/forecast
String lat = "55.3133";
String lon = "0.9022";

void setup() {
  size(450, 600);
  frameRate(0.5);
}

void draw() {
  background(255);
  fill(0);
  textSize(30);
  text("FAVERSHAM", 10, 30);

  //TIMER
  if (millis() < 5500) {
    println("Looked at: " + millis() + " ms, at time: " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2));
  } else {
    delay(60000);
    //println("Looked at: " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2));
  }

  //DATA SOURCE
  String All = (url + "lat=" + lat + "&lon=" + lon + "&units=metric&APPID=" + APPID);

  JSONObject json = loadJSONObject(All);
  println(All);

  //COORDINATES
  float FavLong = json.getJSONObject("coord").getFloat("lon");
  float FavLat = json.getJSONObject("coord").getFloat("lat");
  textSize(20);
  fill(0);
  text("Longitude: ", 10, 70);
  text(FavLong + " °", 160, 70);
  text("Latitude: ", 10, 90);
  text(FavLat + " °", 160, 90);


  //WEATHER
  String FavDesc1 = json.getJSONArray("weather").getJSONObject(0).getString("main");
  String FavDesc2 = json.getJSONArray("weather").getJSONObject(0).getString("description");
  text("Description:  ", 10, 160);
  text(FavDesc1 + " - " + FavDesc2, 160, 160);
  float FavClds = json.getJSONObject("clouds").getFloat("all");
  text("Cloud Cover: ", 10, 190);
  text(FavClds + " %", 160, 190);
  float FavVisa = json.getFloat("visibility");
  text("Visibility: ", 10, 210);
  text(nf(FavVisa/1000, 2, 1) + " km", 160, 210);
  float FavTemp = json.getJSONObject("main").getFloat("temp");
  text("Temperature: ", 10, 240);
  text(FavTemp + " °C", 160, 240);
  float FavFeel = json.getJSONObject("main").getFloat("feels_like");
  text("Feels Like: ", 10, 260);
  text(FavFeel + " °C", 160, 260);
  float FavTmin = json.getJSONObject("main").getFloat("temp_min");
  text("Min: ", 10, 290);
  text(FavTmin + " °C", 160, 290);
  float FavTmax = json.getJSONObject("main").getFloat("temp_max");
  text("Max: ", 10, 310);
  text(FavTmax + " °C", 160, 310);

//IF/ELSE SOLUTION - NOT WORKING
  //text("Rain: ", 10, 340);
  //Float FavRain = json.getJSONObject("rain").getFloat("1h");
  //if (FavRain == null) {
  //  text("0.0 mm/hr", 160, 340);
  //} else {
  //  text(FavRain + " mm/hr", 160, 340);
  //}

//BOOLEAN SOLUTION - NOT WORKING
//if (rainy) {
//  float FavRain = json.getJSONObject("rain").getFloat("1h");
//  text("Rain: ", 10, 340);
//  text(FavRain + " mm/hr", 160, 340);
//}
//if (!rainy) {
//  text("Rain: ", 10, 340);
//  text("no rain", 160, 340);
//}

//SIMPLE CODE - ONLY WORKS WHEN THERE IS RAIN!
  float FavRain = json.getJSONObject("rain").getFloat("1h");
  text("Rain: ", 10, 340);
  text(FavRain + " mm/hr", 160, 340);


  float FavWspeed = json.getJSONObject("wind").getFloat("speed");
  text("Wind Speed: ", 10, 370);
  text(FavWspeed + "m/s", 160, 370);
  float FavWdir = json.getJSONObject("wind").getFloat("deg");
  text("Wind Direction: ", 10, 390);
  text(FavWdir + " degrees", 160, 390);
  float FavWgust = json.getJSONObject("wind").getFloat("gust");
  text("Gust Speed: ", 10, 410);
  text(FavWgust + "m/s", 160, 410);
  float FavAirP = json.getJSONObject("main").getFloat("pressure");
  text("Air Pressure: ", 10, 440);
  text(FavAirP + " hPa", 160, 440);
  float FavHum = json.getJSONObject("main").getFloat("humidity");
  text("Humidity: ", 10, 460);
  text(FavHum + " %", 160, 460);
}

Use a try/catch; below as a guide line

try
{
  Float FavRain = json.getJSONObject(“rain”).getFloat(“1h”)
  println(FavRain);
}
catch (exception ex)
{
  println("No rain")
}

You will need to look at the exact details of the catch. I have no access to Processing at the moment.

Method getFloat() has a 2nd defaultValue parameter:

final float favRain = json.getJSONObject("rain").getFloat("1h", 0);
text("Rain: ", 10, 340);
text(favRain + " mm/hr", 160, 340);
1 Like

Thank you both for your contributions. Unfortunately, I seem to still have the same problem of the NullPointerException. I think this is because it is both the actual “rain” parameter as well as the “1hr” parameter that disappear when there is no rain. So, in the case of GoToLoop’s example, the getFloat instruction can’t display its default “0” as the code stumbles on the preceding missing “rain”.

To hopefully make things simpler, I have included a simpler version of my code below, and hopefully properly formatted this time. (I apologise that I didn’t manage this on my previous attempt).

String url = "https://api.openweathermap.org/data/2.5/weather?";
String APPID = "xxxxxxxxxxxxxxxxxxxxxx";
String lat = "55.3133";
String lon = "0.9022";

void setup() {
  size(450, 600);
  frameRate(0.5);
  surface.setLocation(displayWidth - 450, 0);
}

void draw() {
  background(255);
  fill(0);
  textSize(20);
  text("FAVERSHAM", 10, 30);

  //CHECK EVERY MINUTE
  if (millis() < 5500) {
    println("Looked at: " + millis() + " ms, at time: " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2));
  } else {
    delay(60000);
    //println("Looked at: " + nf(hour(), 2) + ":" + nf(minute(), 2) + ":" + nf(second(), 2));
  }

  //DATA SOURCE
  String All = (url + "lat=" + lat + "&lon=" + lon + "&units=metric&APPID=" + APPID);
  JSONObject json = loadJSONObject(All);
  println(All);

  //TEMPERATURE
  float FavTemp = json.getJSONObject("main").getFloat("temp");
  text("Temperature: ", 10, 100);
  text(FavTemp + " °C", 160, 100);

  //RAIN - Only works when there is rain!
  final float favRain = json.getJSONObject("rain").getFloat("1h", 0);
  text("Rain: ", 10, 180);
  text(favRain + " mm/hr", 160, 180);


  //If/Else solution - NOT WORKING
  //text("Rain: ", 10, 180);
  //Float FavRain = json.getJSONObject("rain").getFloat("1h");
  //if (FavRain == null) {
  // text("0.0 mm/hr", 160, 180);
  //} else {
  // text(FavRain + " mm/hr", 160, 180);
  //}

  //Boolean Solution - NOT WORKING
  //if (rainy) {
  // float FavRain = json.getJSONObject("rain").getFloat("1h");
  // text("Rain: ", 10, 180);
  // text(FavRain + " mm/hr", 160, 180);
  //}
  //if (!rainy) {
  // text("Rain: ", 10, 180);
  // text("no rain", 160, 180);
  //}

  //WIND SPEED
  float FavWspeed = json.getJSONObject("wind").getFloat("speed");
  text("Wind Speed: ", 10, 260);
  text(FavWspeed + "m/s", 160, 260);
}

If you’re suspicious your JSON’s key “rain” may not always be available, you may use methods hasKey() or isNull() as a safety net:

final float favRain = json.hasKey("rain") ? // !json.isNull("rain") ?
                      json.getJSONObject("rain").getFloat("1h") : 0;

P.S.: The fix above assumes child key “1h” is always available when its parent key “rain” exists.
If there’s any chance subkey “1h” might be optional, use the version of method getFloat() w/ 2 parameters: .getFloat("1h", 0) : 0;

1 Like

Brilliant! Thank you GoToLoop (and the others who chipped in. I learnt a lot.)
My working solution for the sometimes missing rain parameter is as follows:

  //RAIN
  final float favRain = json.hasKey("rain") ? // !json.isNull("rain") ?
    json.getJSONObject("rain").getFloat("1h") : 0.0;
  text("Rain: ", 10, 180);
  text(favRain + " mm/hr", 160, 180);

That’s just a comment showing you can replace json.hasKey("rain") ? for that. :nerd_face:

final float favRain = json.isNull("rain") ? 0 :
                      json.getJSONObject("rain").getFloat("1h");

Java automatically upcasts whole numbers to fractional 1s. So just 0 is enough. :wink:

Thank you again. Much appreciated.

1 Like