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 :)
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:
You passed a class ListTeleServices.class, and the JSON response starts with an object {}... so far everything is OK!
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:
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)
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;
change this:
private HashMap<String, TeleService> listTeleServices;
to this
private HashMap<String, TeleService> listTeleServices = new HashMap<String,TeleService>();
Initialize the
private HashMap<String, TeleService> listTeleServices;
as
private HashMap<String, TeleService> listTeleServices = new HashMap<>();
In your current code , you are trying to invoke get() on the null object reference, which throws NullPointerException , as you are trying to invoke instance method on a null object.
Related
I am trying to map below JSON to a POJO Class using Gson library. Below is the JSON response and POJO Class and mapping done
import java.util.Map;
import com.google.gson.JsonElement;
public class DataResponse {
private String $status;
private Map<String, JsonElement> $payload;
public String get$status() {
return $status;
}
public void set$status(String $status) {
this.$status = $status;
}
public Map<String, JsonElement> get$payload() {
return $payload;
}
public void set$payload(Map<String, JsonElement> $payload) {
this.$payload = $payload;
}
}
Here is the Sample JSON.
{
"$status": "OK",
"$payload": {
"$nextStart": "123",
"$results": [
{
"$key": "101",
"score": 3,
"to": "Test1"
},
{
"$key": "102",
"score": 4,
"to": "Test2"
},
]
}
}
Below is the mapping done. Is there some problem with POJO class definition. Since I cannot get all the elements of JSON response mapped to the innermost element from the response. Appreciate your support in providing useful suggestions.
Gson gson = new Gson();
DataResponse dataResponse = gson.fromJson(EntityUtils.toString(response.getEntity()),
DataResponse.class);
While working with marshalling and unmarshalling, it is always good to have a model defined as:
public class DataResponse {
private String $status;
private Payload $payload;
// getters and setters
}
class Payload {
private String $nextStart;
private List<Result> $results;
// getters and setters
}
class Result {
private String $key;
private String score;
private String to;
// getters and setters
}
Now when you convert json to POJO as:
Gson gson = new Gson();
DataResponse dataResponse = gson.fromJson(EntityUtils.toString(response.getEntity()), DataResponse.class);
it can easily convert it.
Also, believe me, it is good for processing in your further code!
Update: if you really want to convert json to Map, then you can do something like this:
import java.lang.reflect.Type;
import com.google.gson.reflect.TypeToken;
Type type = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> myMap = gson.fromJson("{'key':'value'}", type);
Substitute json string there.
I have to deserialize json like this:
{
"key1" : [
{
"hash1" : "value1_1",
"hash2" : "value1_2",
...
"hashN" : "value1_3",
"date" : "dateValue1"
},
{
"hash1" : "value2_1",
"hash2" : "value2_2",
...
"hashN" : "value2_3",
"date" : "dateValue2"
},
...
],
"key2": {
"description" : {
"hash1" : {
"description1" : "some text",
"description2" : "some text",
},
...
"hashN" : {
"description1" : "some text",
"description2" : "some text",
}
}
}
}
That json have set of unknow keys: hash1, hash2, ... hash2, and set of know keys: key1, key2, description, date, description1, description2.
I work with some custom rest client which use default GSON configuration to deserialize jsons to objects. And I can't change that configuration.
Using this rest client looks like this:
restClient.makeRequest(requestData, DataResponse.class, new RestResponseListener<DataResponse>()
{
#Override
public void onSuccessfulResponse(DataResponse responseData)
{
}
});
DataResponse class have to inherit from Response class from rest client package.
That rest client can't deserialize jsons like above so I decide to try deserializing to String or JsonObject and next in onSuccessfulResponse use custom deserializer.
I try to create below class to hold response:
public class DataResponse extends Response
{
private String key1;
private String key2;
public DataResponse()
{
}
}
Unfortunately I get exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 14 path
The question is, how to deserialize array from key1 and object from key2 to strings.
Or maybe is another solution.
Follow Lyubomyr Shaydariv advice I found two solutions.
Using JsonElement class
JsonElement is of course abstract so I have to use subclasses as below
public class DataResponse extends Response
{
private JsonArray key1;
private JsonObject key2;
public DataResponse()
{
}
//getters
}
In rest client callback I handled this data like this:
restClient.makeRequest(requestData, DataResponse.class, new RestResponseListener<DataResponse>()
{
#Override
public void onSuccessfulResponse(DataResponse responseData)
{
Type mapType = new TypeToken<Map<String, String>>(){}.getType();
Gson gson = new Gson();
for(JsonElement element : responseData.getKey1())
{
Map<String, String> map = gson.fromJson(element, mapType);
//do things
}
}
});
Match DataResponse class to json structure
I don't know why it doesn't work previously, but now does:
public class DataResponse extends Response
{
private List<Map<String, String>> key1;
private Key2SchemaClass key2;
public DataResponse()
{
}
public static class Key2SchemaClass
{
private List<Map<String, Description>> description;
}
public static class Description
{
private String description1;
private String description2;
}
//getters
}
I have JSON file looks like
{
"SUBS_UID" : {
"featureSetName" : "SIEMENSGSMTELEPHONY MULTISIM",
"featureName" : "MULTISIMIMSI",
"featureKey" : [{
"key" : "SCKEY",
"valueType" : 0,
"value" : "0"
}
]
},
}
So the key is a String "SUBS_ID" and the value is a model called FeatureDetails which contains attributes "featureSetName,featureName,...".
So i read from the JSON file using google.json lib like this,
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, HashMap.class);
then I'm trying to loop over this HashMap getting the value and cast it to my FeatureDetails model,
for (Map.Entry entry : featuresFromJson.entrySet()) {
featureDetails = (FeatureDetails) entry.getValue();
}
and here is my FeatureDetails Model,
public class FeatureDetails {
private String featureSetName;
private String featureName;
private ArrayList<FeatureKey> featureKey;
private String groupKey;
private String groupValue;
public FeatureDetails() {
featureKey = new ArrayList<FeatureKey>();
}
public ArrayList<FeatureKey> getFeatureKey() {
return featureKey;
}
public void setFeatureKey(ArrayList<FeatureKey> featureKey) {
this.featureKey = featureKey;
}
public String getGroupKey() {
return groupKey;
}
public void setGroupKey(String groupKey) {
this.groupKey = groupKey;
}
public String getGroupValue() {
return groupValue;
}
public void setGroupValue(String groupValue) {
this.groupValue = groupValue;
}
public String getFeatureName() {
return featureName;
}
public void setFeatureName(String featureName) {
this.featureName = featureName;
}
public String getFeatureSetName() {
return featureSetName;
}
public void setFeatureSetName(String featureSetName) {
this.featureSetName = featureSetName;
}
}
but i got an exception "com.google.gson.internal.LinkedHashTreeMap cannot be cast to com.asset.vsv.models.FeatureDetail".
try this:
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, new TypeToken<Map<String, FeatureDetails>>() {}.getType());
and when you going through your hash map do this:
for (Map.Entry<String, FeatureDetails> entry : featuresFromJson.entrySet()) {
FeatureDetails featureDetails = entry.getValue();
}
The reason you're seeing this is because you're telling GSON to deserialize the JSON structure using the structure of a HashMap in the line
... = new Gson().fromJson(JSONFeatureSet, HashMap.class);
^^
Right here
As a result, GSON has no idea that the sub objects in the JSON are anything other than simple key-value pairs, even though the structure may match the structure of your FeatureDetails object.
One solution is to create a model which wraps your FeatureDetails object, which will act as the root of the entire structure. This object might look something like this:
public class FeatureDetailsRoot{
private FeatureDetails SUBS_UID; // poor naming, but must match the key in your JSON
}
And finally, you'd pass that model's class:
= new Gson().fromJson(JSONFeatureSet, FeatureDetailsRoot.class)
Update
In answer to your question in the comment regarding the ability to add / have multiple FeatureDetails objects, the problem presently is that your JSON does not reflect that kind of structure. Meaning, the "SUBS_UID" key points to a single object, not an array objects. If you would like to have this ability, then your json will need to be altered so that it shows an array of objects, like this:
{
"SUBS_UID" : [{
"featureSetName" : "Feature set name #1",
...attributes for feature #1
},
{
"featureSetName" : "Feature set name #2",
...attributes for feature #2
},
...other features
]
}
And then you can simply alter the root class so that it contains a list of FeatureDetails objects, like so:
public class FeatureDetailsRoot{
private List<FeatureDetails> SUBS_UID;
}
Let me know if that makes sense (or whether I've misunderstood you)
(objectName as Map<String, Any>).get("fieldName")
the code is in Kotlin:
use val type = object : TypeToken<HashMap<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
instead of val type = object : TypeToken<Map<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
note: HashMap instead of Map
I am trying to deserialize the json into MyClass object but every time I get the exception though I am able to serialize the JSON object.
**com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.util.LinkedHashMap out of START_ARRAY token**
The json I am trying to deserialize is
{
"siteName": "avisports",
"csAssetTypes": [
"Content_C",
[
"name",
"description",
"subheadline",
"abstract",
"body",
"headline",
"subheadline",
"avi_content_title",
"avi_content_body",
"avi_content_headline",
"avi_content_abstract"
]
]
}
my Bean is something like
public class GlContentServerConfig{
private String siteName;
private Map<String, List<String>> csAssetTypes = new HashMap<String, List<String>>();
//getters ans setters
}
and my service method is
#Override
public GlContentServerConfig getConfiguredAttributes(String siteName, String assetType) throws Exception {
if (this.configMap == null) {
this.configMap = this.configDAO.getAllConfigs();
}
GlContentServerConfig config = new GlContentServerConfig();
config.setSiteName(siteName);
config.setCsAssetTypes(csService.getAssetTypeAttributeList(assetType));
GloballinkConfig obj = this.configMap.get(siteName);
if (obj != null) {
String jsonValue = obj.getGlConfigValue();
config=this.mapper.readValue(jsonValue, GlContentServerConfig.class); // error comes from this line
//List<GlContentServerConfig> glconfigList= this.mapper.readValue(jsonValue, new TypeReference<GlContentServerConfig>(){});
//List<GlContentServerConfig> glconfigList = this.mapper.readValue(jsonValue, this.mapper.getTypeFactory().constructCollectionType(List.class, GlContentServerConfig.class));
System.out.println("final : "+glconfigList.toString());
}
return config;
}
I have tried all most all of the permutations and combinations. Few of them are commented in the code.
I am unable to figure out the way to use Jackson. please help me out.
add #JsonProperty("csAssetTypes") to your Bean :
public class GlContentServerConfig{
private String siteName;
#JsonProperty("csAssetTypes")
private Map<String, List<String>> csAssetTypes = new HashMap<String, List<String>>();
//getters ans setters
}
I have JSON file looks like
{
"SUBS_UID" : {
"featureSetName" : "SIEMENSGSMTELEPHONY MULTISIM",
"featureName" : "MULTISIMIMSI",
"featureKey" : [{
"key" : "SCKEY",
"valueType" : 0,
"value" : "0"
}
]
},
}
So the key is a String "SUBS_ID" and the value is a model called FeatureDetails which contains attributes "featureSetName,featureName,...".
So i read from the JSON file using google.json lib like this,
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, HashMap.class);
then I'm trying to loop over this HashMap getting the value and cast it to my FeatureDetails model,
for (Map.Entry entry : featuresFromJson.entrySet()) {
featureDetails = (FeatureDetails) entry.getValue();
}
and here is my FeatureDetails Model,
public class FeatureDetails {
private String featureSetName;
private String featureName;
private ArrayList<FeatureKey> featureKey;
private String groupKey;
private String groupValue;
public FeatureDetails() {
featureKey = new ArrayList<FeatureKey>();
}
public ArrayList<FeatureKey> getFeatureKey() {
return featureKey;
}
public void setFeatureKey(ArrayList<FeatureKey> featureKey) {
this.featureKey = featureKey;
}
public String getGroupKey() {
return groupKey;
}
public void setGroupKey(String groupKey) {
this.groupKey = groupKey;
}
public String getGroupValue() {
return groupValue;
}
public void setGroupValue(String groupValue) {
this.groupValue = groupValue;
}
public String getFeatureName() {
return featureName;
}
public void setFeatureName(String featureName) {
this.featureName = featureName;
}
public String getFeatureSetName() {
return featureSetName;
}
public void setFeatureSetName(String featureSetName) {
this.featureSetName = featureSetName;
}
}
but i got an exception "com.google.gson.internal.LinkedHashTreeMap cannot be cast to com.asset.vsv.models.FeatureDetail".
try this:
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, new TypeToken<Map<String, FeatureDetails>>() {}.getType());
and when you going through your hash map do this:
for (Map.Entry<String, FeatureDetails> entry : featuresFromJson.entrySet()) {
FeatureDetails featureDetails = entry.getValue();
}
The reason you're seeing this is because you're telling GSON to deserialize the JSON structure using the structure of a HashMap in the line
... = new Gson().fromJson(JSONFeatureSet, HashMap.class);
^^
Right here
As a result, GSON has no idea that the sub objects in the JSON are anything other than simple key-value pairs, even though the structure may match the structure of your FeatureDetails object.
One solution is to create a model which wraps your FeatureDetails object, which will act as the root of the entire structure. This object might look something like this:
public class FeatureDetailsRoot{
private FeatureDetails SUBS_UID; // poor naming, but must match the key in your JSON
}
And finally, you'd pass that model's class:
= new Gson().fromJson(JSONFeatureSet, FeatureDetailsRoot.class)
Update
In answer to your question in the comment regarding the ability to add / have multiple FeatureDetails objects, the problem presently is that your JSON does not reflect that kind of structure. Meaning, the "SUBS_UID" key points to a single object, not an array objects. If you would like to have this ability, then your json will need to be altered so that it shows an array of objects, like this:
{
"SUBS_UID" : [{
"featureSetName" : "Feature set name #1",
...attributes for feature #1
},
{
"featureSetName" : "Feature set name #2",
...attributes for feature #2
},
...other features
]
}
And then you can simply alter the root class so that it contains a list of FeatureDetails objects, like so:
public class FeatureDetailsRoot{
private List<FeatureDetails> SUBS_UID;
}
Let me know if that makes sense (or whether I've misunderstood you)
(objectName as Map<String, Any>).get("fieldName")
the code is in Kotlin:
use val type = object : TypeToken<HashMap<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
instead of val type = object : TypeToken<Map<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
note: HashMap instead of Map