Parsing JSON using Google GSON: reading values directly from child objects - java

I'm having trouble parsing the following JSON with Google's Gson:
{"Name":
{"object1":
{"field1":"17",
"field2":"360",
"field3":"19",
"field4":"sun",
"field5":"rain"
}
}
}
I have tried the following to get the value of field1 but it doesn't work
#SerializedName("Name/object1/field1")
public int fieldOne;
What am I doing wrong?

Your objects have to conserve the hierarchy of your json instructions. For your example, it would be something like this:
public class Object {
#SerializedName("field1")
public String fieldOne;
#SerializedName("field2")
public String fieldTwo;
#SerializedName("field3")
public String fieldThree;
#SerializedName("field4")
public String fieldFour;
}
public class Name {
#SerializedName("object1")
public Object obj;
}
public class GsonObj {
#SerializedName("Name")
public Name name;
}
Using the following call:
String json = "{\"Name\":{" +
"\"object1\":{" +
"\"field1\":\"17\",\"field2\":\"360\",\"field3\":\"19\",\"field4\":\"sun\",\"field5\":\"rain\"}}}";
Gson gson = new Gson();
GsonObj jsonResult = gson.fromJson(json, GsonObj.class);
Log.d("test", "field one: "+jsonResult.name.obj.fieldOne);
Log.d("test", "field two: "+jsonResult.name.obj.fieldTwo);
Log.d("test", "field three: "+jsonResult.name.obj.fieldThree);
Log.d("test", "field four: "+jsonResult.name.obj.fieldFour);

You have invalid JSON. JSON may either start with { or [ so you need to wrap your string with another pair of {}.
A good practice is to always check your data first. I often use this here:
http://jsonlint.com/

I don't think you can have "Name/object1/field" you have to specify key name directly without hierarchy.
refer How to parse dynamic JSON fields with GSON?

Related

GSON - print all fields that are annotated as deserialized=true

I have a Java EE project that is using GSON library (Google's library for processing of JSON objects).
In my entity classes I use #Expose annotation to control which fields are considered by GSON. I also use serialize/deserialize properties on that annotation to control which fields are considered when serializing a Java object to JSON and which fields are considered when deserializing JSON objects to Java objects. For example:
public class Movie {
#Expose(serialize=true, deserialize=false)
#Id
#GeneratedValue
private long id;
#Expose(serialize=true, deserialize=true)
private String name;
#Expose(serialize=true, deserialize=true)
private String genre;
#Expose(serialize=false, deserialize=true)
private String secretID;
}
Here when I send the JSON object to be deserialized into Java object I send an object like this:
{
"name": "Memento",
"genre": "thriller",
"secretID": "123asd"
}
And, when I serialize Java object to JSON I get something like this:
{
"id": 1,
"name": "Memento",
"genre": "thriller"
}
I have this Java code:
public static void main(String[] args) {
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().setPrettyPrinting().create();
String json = gson.toJson(new Movie());
System.out.println(json);
}
that generates this as it's output:
{
"id": 0,
"name": "",
"genre": ""
}
Those are fields that are marked to be serialized. However, what if I need to print out all of the fields that are marked to be deserialized, so that I can easier create a JSON object that will be used as input when creating new Movies.
The desired output is this:
{
"name": "",
"genre": "",
"secretID": ""
}
Note: I don't want to change serialize/deserialize properties on #Expose annotations because they are set to how my application needs to work. I just need an easy way to generate a template JSON objects that will be used as input to my application, so I don't have to type it manually.
You could implement more generic ExclusionStrategy like:
#RequiredArgsConstructor
public class IncludeListedFields implements ExclusionStrategy {
#NonNull
private Set<String> fieldsToInclude;
#Override
public boolean shouldSkipField(FieldAttributes f) {
return ! fieldsToInclude.contains(f.getName());
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
then use it like:
Set<String> fieldsToInclude =
new HashSet<>(Arrays.asList("name", "genre", "secretID"));
ExclusionStrategy es = new IncludeListedFields(fieldsToInclude);
Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls()
.addSerializationExclusionStrategy(es).create();
Note following things:
You should not now use the builder method .excludeFieldsWithoutExposeAnnotation.
By default Gson does not serialize fileds with null values so you need to use builder method .serializeNulls(). This does not generate Json with string values "" but just null.
In your example Json fields contained empty strings as values but you did not introduce default constructor Movie() that would initialize field values to empty strings so they remain null. But if you initialize them - say to empty string ""- then they are not null & you do not need to use builder method .serializeNulls().
BUT if you really need and want only to serialize based on #Expose(deserialize=true) then the ExclusionStrategy can be just:
public class PrintDeserializeTrue implements ExclusionStrategy {
#Override
public boolean shouldSkipField(FieldAttributes f) {
Expose annotationExpose = f.getAnnotation(Expose.class);
if(null != annotationExpose) {
if(annotationExpose.deserialize())
return false;
}
return true;
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}

Converting LinkedHashMap<String,MyClass> to Java Object

How can I convert the JSON string like this:
{ "summary": {
"totalMR":4.599000000000903E12,
"totalMA":1.9174920000386694E11,
"totalQA":5.1111111181E9,
"totalQR":1.000020666115264E11
},
"result": [{},{}],
"success":"true",
"total":49
}
to a Java object. I went through many similar posts and implemented constructors but couldn't find the proper explanation of why I'm unable to De-serialize the JSON.
Am I doing anything wrong?
My Class:
public class expResponse {
private String success;
private String total;
private ArrayList<LinkedHashMap<String,Object>> result;
private LinkedHashMap<String,SummaryResponse> summary;
// Constructor: public expResponse(){}
// Getter and Setter
}
public class SummaryResponse {
private Float totalQR;
private Float totalQA;
private Float totalMR;
private Float totalMA;
public SummaryResponse(){}
// Setter and Getter
}
My Code:
private expResponse processResult(String result) throws IOException{
ObjectMapper objectMapper = new ObjectMapper();
expResponse expResponseObj =
objectMapper.readValue(result, expResponse.class);
return expResponseObj;
The json you posted would not deserialize into a map of SummaryResponse objects, but rather an individual SummaryResponse object. To make your binding work, you would have to have json that looked something like this:
{
...
'summary': {
'summary1': {"totalMR":4.599000000000903E12,"totalMA":1.9174920000386694E11,"totalQA":5.1111111181E9,"totalQR":1.000020666115264E11}
'summary2': {"totalMR":4.599000000000903E12,"totalMA":1.9174920000386694E11,"totalQA":5.1111111181E9,"totalQR":1.000020666115264E11}
}
...
}
Alternatively, if you need to make your Java class conform to the json you provided, you simply need to change the declaration of summary:
private SummaryResponse summary;
Field summary in your json is an object of type SummaryResponse and not a LinkedHashMap.
public class ExpResponse {
private String success;
private String total;
private ArrayList<LinkedHashMap<String,Object>> result;
private Summary summary;
}
I don't think you have a problem in the code. Your input fails because it is not in the correct format. If you try to write the same values from an object with the same values to string you get something like:
{
"success":"true",
"total":"49",
"result":null,
"summary":{
"one_summary":{
"totalQR":2000.0,
"totalQA":1500.0,
"totalMR":1000.0,
"totalMA":500.0
}
}
}
And the major difference is the one summary. This is because summary is a map and maps need a key for each entryset. That means that summary is your map which has a one_summary key.
Is it the SummaryResponse that can't be deserialised?
I guess your attributes should have the same name "totalMR", "totalMA"....
or you should use an annotation JsonProperty(value="totalMR") and so on.

How to use fromGson with arrays

I have the json:
{"test": [{"param1": "param"}],
"test2": "test",
"test3": "test2"}
how can I get fromGson to parse the arrays in a json correctly so that it populates the arrays into an array of a particular object type.
So test should map to TestObject[] test;
test2 -> String test2;
test3 -> String test3;
I tried gson.fromJson(response, TestData.class); that gave me an error: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 664
( that the fromJson is not parsing the array in the json correctly).
I also tried going through this post:
Parsing JSON array into java.util.List with Gson
But got confused.
but that did not work
Answer
public class TestClass {
public String param1;
}
public class TestMap {
public TestClass[] Test;
public String test2;
public String test3;
}
public Collection<TestMap> collectionFromJSON(String jsonString) {
Gson gson = new Gson();
Collection<TestMap> testMaps = gson.fromJson(jsonString, new TypeToken<Collection<TestMap>>() {
}.getType());
return testMaps;
}
public TestMap singleFromJSON(String jsonString) {
Gson gson = new Gson();
Journal testMap = gson.fromJson(jsonString, TestMap.class);
return testMap;
}
Okay, I will explain something here.
Explain
Normally if you have a nested array of objects inside another object, you need to create a new class. Ie. if Journal has Categories, Category has two other properties, title and index.
The class will be like
class Journal {
public Category[] categories;
....
}
class Category {
public String title;
public Integer index;
....
}
So now, the mistake is to confused Array of String and Array of Class. If the json is
{"Test", ["Test1", "Test2"...]
"Test2": "Test3"}
You can define it as an array of String. But it is a nested JSON Object, so it has to be class again.
Without seeing your class, I'm going to guess. This error
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected a string but was BEGIN_OBJECT at line 1 column 664
means you are trying to deserialize a String, so something like
"some string"
but what the JSON actually contained was a JSON object, something like
{"name" : "value"}
Your class is probably mapped wrong. It should be
String test2;
String test3;
TestObject[] test;
where TestObject should be
public class TestObject {
String param1;
}
since the JSON contains an JSON string called param1.
python has a very useful module called json
try var TestObject=json.parse()

How to create more than one object with Gson and how can different objects explicit referenced in a Java class by using one JSON file?

I have a JSON file like this:
{"id" : "1", "name" : "David"} // this represent testdata for the class Person
{"accountid" : "1188", "accountnumber" : "119295567"} // this represent testdata for the class account
{"id" : "22", "date" : "22.11.2013"} // this represent testdata for the class transaction
Now, I have three Java classes (with suitable attributes like in the JSON file and get- and set methods)
Person
Account
Transaction
I have written a Junit Test and will use the JSON file. I will generate three different objects by using only one JSON file.
How can I do this using Gson? This is what I tried so far to deserialize a Person object.
Gson gson = new GsonBuilder().create();
String jsonTestFile = FileUtils.readFileToString(new File(this.pathForJsonTestFile
+ "testFile.json"));
Person person = gson.fromJson(jsonTestFile,
Person.class);
But how can I explicit create the account object or the transaction object or the person object depending from the JSON?
If you had a field in your JSON that tells you what kind of object you are trying to parse, it will be "easier", however, since in your data your can distinguish object by field structure, you can parse your JSON according to it. I mean, if you have accountid field, it's an Account class, and so on.
So, what your have to do, is to look into your JSON and decide what kind of class you want to use to deserialize. To do something like that, you can use JsonParser class that returns you a browsable tree of objects and then fire a standard Gson deserialization.
I prepared a code that you can copy&run in your IDE to show how to do it.
package stackoverflow.questions.q19997365;
import com.google.gson.*;
public class Q19997365 {
public static class Person {
String id;
String name;
#Override
public String toString() {
return "Person [id=" + id + ", name=" + name + "]";
}
}
public static class Account {
String accountid;
String accountnumber;
#Override
public String toString() {
return "Account [accountid=" + accountid + ", accountNumber=" + accountnumber + "]";
}
}
public static class Transaction {
String id;
String date;
#Override
public String toString() {
return "Transaction [id=" + id + ", date=" + date + "]";
}
}
/**
* #param args
*/
public static void main(String[] args) {
String json1 = "{\"id\" : \"1\", \"name\" : \"David\"}"; // this represent testdata for the class Person
String json2 = "{\"accountid\" : \"1188\", \"accountnumber\" : \"119295567\"}"; // this represent testdata for the class account
String json3 = "{\"id\" : \"22\", \"date\" : \"22.11.2013\"}"; // this represent testdata for the class transaction
System.out.println(extractFromJson(json1));
System.out.println(extractFromJson(json2));
System.out.println(extractFromJson(json3));
}
private static Object extractFromJson(String json) {
Gson g = new Gson();
JsonObject e = new JsonParser().parse(json).getAsJsonObject();
if (e.get("name") != null)
return g.fromJson(json, Person.class);
if (e.get("accountid") != null)
return g.fromJson(json, Account.class);
if (e.get("date") != null)
return g.fromJson(json, Transaction.class);
return null;
}
}
and this is my execution:
Person [id=1, name=David]
Account [accountid=1188, accountnumber=119295567]
Transaction [id=22, date=22.11.2013]
The key part is extractFromJson method that does all the job. It uses a JsonParser to snoop into the JSON string and then calls a Gson instance to do the right deserialization.
Three final notes
the method instantiates Gson every time, is not efficient, but here I want to show you the concept, you can easily improve this.
your date field is not a kind of date that Gson can parse by default, you need change date format for that, see this for example
extractFromJson method is something like a factory pattern, in this pattern you cannot know what kind of object will be returned. So Object is the return type, you need an instanceof + cast to manage it correctly.

Json Data assign to java class

I'm having problem to assign json data into java class.Please do help anyone,
My java class is like,
public class ListofGridRecords<T> {
public int Totalrecords;
public List<T> GridRecords;//using TraderTransaction class.
}
and TraderTransaction class is,
public class TraderTransaction {
public Date AddedTime;
public String TransactId;
public TransactStatus Status;
public String OtherPartyAccountNo;
public Double AmountPaid;
public Double AmountRecieved;
public Double ClosingBalance;
public TransactionTypes TransType;
public String Narration;
public TraderTransaction() {
super();
}
}
and my json conversion function look like,
JsonObject returndata = JsonObject.parse(responseString);
String operationresult = returndata.get("OperationResult").toString();
if (Result.values()[Integer.parseInt(operationresult)] == Result.Success) {
Gson gson = new Gson();
#SuppressWarnings("unchecked")
ListofGridRecords<TraderTransaction> traderlist =
gson.fromJson(returndata.get("ResultData").toString(), ListofGridRecords.class);
Log.i("LIST DATA:", "" + traderlist);
for (TraderTransaction trader: traderlist.GridRecords) {
HashMap<String, String> map = new HashMap<String, String>();
map.put(TRANS_FIRST_COLUMN, currentformatter.format(trader.AddedTime));
map.put(TRANS_SECOND_COLUMN, trader.TransactId);
map.put(TRANS_THIRD_COLUMN, trader.OtherPartyAccountNo);
map.put(TRANS_FOURTH_COLUMN, trader.AmountPaid.toString());
map.put(TRANS_FIFTH_COLUMN, trader.AmountRecieved.toString());
map.put(TRANS_SIXTH_COLUMN, OpenOrClosed.values()[Integer.parseInt(trader.TransType.toString())].toString());
list.add(map);
}
}
I'm getting conversion error at for (TraderTransaction trader : traderlist.GridRecords).
My Json data look like,
{
"Messages":"RESULTS_RETRIEVAL_SUCCESSFULL",
"OperationResult":0,
"ResultData":{
"GridRecords":[
{
"AddedBy":"Distributor-9787457361-Rathinavel",
"AddedTime":"2013-04-12T16:26:24.0140117",
"AmountPaid":0.0,
"AmountRecieved":10000.0,
"ClosingBalance":10000.0,
"Narration":null,
"OtherPartyAccountNo":"0102849015327675",
"Status":2,
"TransType":2,
"TransactId":"TDRF483679051236"
},
{
"AddedBy":"Distributor-9787457361-Rathinavel",
"AddedTime":"2013-04-12T16:20:54.8681857",
"AmountPaid":0.0,
"AmountRecieved":0.0,
"ClosingBalance":0.0,
"Narration":null,
"OtherPartyAccountNo":"0102849015327675",
"Status":0,
"TransType":2,
"TransactId":"TDRF706925413802"
}
],
"Totalrecords":2
},
"UpdateAvailable":"0"
}
In order to parse your JSON, I'd use a slightly different strategy. As you seem to be interested in parsing only the "ResultData", I'd create classes to wrap the response, very similar to those you have already created, namely:
public class Response {
#SerializedName("ResultData")
public ResultData resultData;
}
and,
public class ResultData {
#SerializedName("GridRecords")
public List<GridRecord> gridRecords;
#SerializedName("Totalrecords")
public int totalrecords;
}
and,
public class GridRecord {
#SerializedName("AddedTime")
public String addedTime;
#SerializedName("TransactId")
public String transactId;
//other fields...
}
and other classes if necessary...
Then, in order to parse your JSON reponse, you just have to do:
Gson gson = new Gson();
Response data = gson.fromJson(responseString, Response.class);
and you'll be able to access any field, for example:
data.resultData.gridRecords.transactId;
Note 1: If you are interested in more fields of the JSON response, you just have to add more fields to your wrap classes, according to the JSON response...
Note 2: I've changed the type of addedTime to String, instead of Date because it throws an exception for unparseable date. Anyway I usually leave the types in the Response objects as simple String and then in the class from where I retrieve the response, I do the correct formatting while creating my objects, for example, when you put the values in your Map...
Note 3: The use of the annotation #SerializedName is interesting to separate the name of a field in the JSON response and in your app, in order to follow Java naming conventions, which your attributes are not following...
Note 4: You shouldn't use public attributes in your classes. It's more recommendable to use private/protected attributes and their correspondent getters and setters...

Categories

Resources