Java – NullPointerException : JSON Parsing in JAVA using GSON

gsonjavajsonnullpointerexception

I want to parse a JSON File through java using the Api GSON to get the last fields of the JSON file :

descriptor.json :

{
    "Teleservice_1" : {
        "Record_1" : {
            "method_name" : "mehdi",
            "method_params": ["param1",2,"param3"]
        },
        "Record_2" : {
            "method_name" : "mkyong",
            "method_params": [3,"param2"]
        },
        "Record_3" : {
            "method_name" : "amine",
            "method_params": [3,"param1","param2"]
        }
    },
    "Teleservice_2" : {
        "Record_11" : {
            "method_name" : "mehdi1",
            "method_params": ["param11",22,"param33"]
        },
        "Record_22" : {
            "method_name" : "mkyong1",
            "method_params": [33,"param22"]
        },
        "Record_33" : {
            "method_name" : "amine1",
            "method_params": [33,"param11","param22"]
        }
    },
    "Teleservice_3" : {
        "Record_111" : {
            "method_name" : "mehdi2",
            "method_params": ["param111",222,"param333"]
        },
        "Record_222" : {
            "method_name" : "mkyong2",
            "method_params": [333,"param222"]
        },
        "Record_333" : {
            "method_name" : "amine2",
            "method_params": [333,"param111","param222"]
        }
    }
}

ListTeleServices.java :

import java.util.HashMap;

public class ListTeleServices {

    private HashMap<String, TeleService> listTeleServices;

    public ListTeleServices() {

    }

    public TeleService getTeleService(String teleserviceName) {
        if(this.listTeleServices.get(teleserviceName) != null) 
            return this.listTeleServices.get(teleserviceName);
        else
            return null;
    }
}

TeleService.java :

import java.util.HashMap;

public class TeleService {

    private HashMap<String, Record> listRecords;

    public TeleService() {

    }

    public Record getRecord(String recordName) {
        if(this.listRecords.get(recordName) != null) 
            return this.listRecords.get(recordName);
        else
            return null;
    }
}

Record.java :

public class Record {

    private String method_name;
    private Object[] method_parameters; 

    public Record(String methodName, Object[] methodParameters) {
        this.method_name = new String(methodName);
        this.method_parameters = methodParameters;
    }

    public String getMethodName() {
        return this.method_name;
    }

    public Object[] getMethodParameters() {
        return this.method_parameters;
    }

    public void setMethodName(String methodName) {
        this.method_name = methodName;
    }

    public void setMethodParameters(Object[] methodParameters) {
        this.method_parameters = methodParameters;
    }
}

And finally my parser class, JSONMainParse.java :

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import com.google.gson.Gson;


public class JSONMainParse {

    public static void main(String[] args) throws FileNotFoundException {

        BufferedReader br = new BufferedReader(new FileReader("/Users/Mehdi/Desktop/descriptor.json"));
        Gson gson = new Gson();
        ListTeleServices teleservices = gson.fromJson(br, ListTeleServices.class);
        String methodName = teleservices.getTeleService("Teleservice_2").getRecord("Record_33").getMethodName();

        System.out.println(methodName);
    }
}

It seems correct to me, and it should display : "amine1" but it gives me a nullPointerException at :

ListTeleServices.getTeleService(ListTeleServices.java:12) which is :

if(this.listTeleServices.get(teleserviceName) != null) 

and at JSONMainParse.main(JSONMainParse.java:15) which is :

String methodName = teleservices.getTeleService("Teleservice_2").getRecord("Record_33").getMethodName();

Do you have any idea about this ? Thank you 🙂

Best Solution

SOLUTION:

You are using more classes than necessary to parse the JSON response! You can delete your classes ListTeleServices and TeleService and keep only your Record class.

Gson gson = new Gson();
Type mapOfMapsType = new TypeToken<Map<String, Map<String, Record>>>() {}.getType();
Map<String, Map<String, Record>> map = gson.fromJson(br, mapOfMapsType);

Finally, in order to get the method name, you have to use:

String methodName = map.get("Teleservice_2").get("Record_33").getMethodName();

EXPLANATION:

When you use your class ListTeleServices to parse the JSON here:

ListTeleServices teleservices = gson.fromJson(br, ListTeleServices.class);

What Gson does is to analise the class ListTeleServices and compare it with the JSON response, so it says:

  1. You passed a class ListTeleServices.class, and the JSON response starts with an object {}... so far everything is OK!

  2. Then it continues parse the JSON, and:

    • In the class ListTeleServices it finds an attribute listTeleServices which is some object (doesn't mind the type for the moment).
    • However, in the JSON response it finds three elements "Teleservice_1", "Teleservice_2" and "Teleservice_3", but none of them has the same name listTeleServices, so Gson skip all these values and assigns null to the attribute listTeleServices...

Remember that Gson needs the names in the JSON response to be the same that those in the class you are using to parse the response.

On the other hand, if you use directly a Map<String, Map<String, Record>>, Gson see:

  1. You passed the type of Map<String, Map<String, Record>>, and the JSON response starts with an object {}... so far everything is OK! (Remember a Map is just an object)

  2. Then it continues parse the JSON, and:

    • In Map<String, Map<String, Record>> it see that there must be some pairs of key (string) and value (some object).
    • And in the JSON response it finds exactly that, some pairs of strings "Teleservice_1", "Teleservice_2" and "Teleservice_3", and some objects {}, so it can keep parsing happily...

P.S: To go further, note that you could have in your class ListTeleServices these attributes:

private HashMap<String, Record> Teleservice_1;
private HashMap<String, Record> Teleservice_2;
private HashMap<String, Record> Teleservice_3;

And it would work well, but this way you can't have an arbitrary number of teleservice ojects...


And by the way, I've also realised other error: in your Response class, the attribute name method_parameters doesn't match the name of the field in the JSON response, which is method_params. You can either change the attribute name or use the annotation:

@SerializedName("method_params")
private Object[] method_parameters;