I am using GSON for the first time. I am trying to deserialise a JSON string into a custom object, but every property of my object is set to null. There are no parsing errors so I think the JSON properties are not mapping to the object?
Here is my code, if anyone could point out where I have gone wrong it would be much appreciated. I have checked everything against tutorial and cannot see the problem. The only thing is that there are more properties in the JSON string than in my object but I hope that does not matter.
JSON string:
{
"_id": "D7D4A7D8219CA25848257C63000A1B50",
"ReportingPerson": "TRAIN2 Ifap",
"InjuredPerson": "TRAIN3 Ifap",
"DateReported": {
"$date": "2014-01-17T00:00:00.000Z"
},
"Company": "test",
"Division": "Learning & Development",
"Site_id": "3CA9AD4E6066388648257B7500047D90",
"Department_id": "724BC4B509E7B61648257363002FD645",
"Area": "Training Room",
"DocNo": "002223",
"CreatedBy": "Ifap TRAIN2",
"DateComposed": {
"$date": "2014-01-17T01:50:23.000Z"
},
"OccurTime": "12:00:00",
"Affiliation": "Employee",
"BriefDescription": "Employee tripped over power lead in computer lab.",
"ThirdPartyInvolvedYN": "No",
"ThirdPartyName": "",
"ThirdPartyAddress": [
""
],
"ThirdPartyTel": "",
"Classification": "Minor Injury",
"Confidential": "",
"ConfidentialMonitors": [
""
],
"IncidentCategory": "2",
"IncidentCategoryPotential": "3",
"ReportableYN": "No",
"ExternalBody": [
""
],
"Authorisor": "",
"WorkSafeConfirmedYN": "No",
"Details": "Fell over cord in computer lab when walking through. Held hand out to brace fall and fell on pinkie finger.",
"Controls": [
"Tape over cord."
],
"Witnesses": [
"No"
],
"Supervisor": "TRAIN1 Ifap",
"IntAuthorisor": "TRAIN3 Ifap",
"IntAuthorisorNext": "",
"AssociatedRisks": {},
"OpenActions": {},
"ClosedActions": {}
}
POJO:
public class Incident {
#SerializedName("_id")
private String _id;
private String docNo;
private String site_id;
private String company;
private String division;
private String department_id;
private Date dateReported;
private String briefDescription;
private String thirdPartyInvolvedYN;
private String supervisor;
private String classification;
private String status;
private String injuredPerson;
private String reportingPerson;
private Date occurDate;
private String occurTime;
//Getters & Setters...
}
Main method:
public Incident convertJSONToBean(String json) {
Incident i = new Incident();
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
Type type = new TypeToken<Incident>(){}.getType();
try {
i = gson.fromJson(json, type);
} catch (JsonSyntaxException e) {
e.printStackTrace();
} catch (JsonIOException e) {
e.printStackTrace();
}
return i;
}
Type is set correctly to Incident.class. But any properties of the resulting Incident object are all null.
I tried commenting out all properties except _id to see if I could get just one to populate but it was still set to null.
In Json format DateComposed & DateReported properties are the Object, You need to create the
either custom model classes for them or write CustomDeserializer class for them.
"DateComposed": { "$date": "2014-01-17T01:50:23.000Z" }
"DateReported": {"$date": "2014-01-17T00:00:00.000Z"}
public class Incident {
#SerializedName("_id")
private String _id;
#SerializedName(value = "ReportingPerson")
// other properties, you need to put #SerializedName on each property
............
// No need to put SerializedName annotation on dateReported & dateComposed
private Date dateReported;
private Date dateComposed;
#SerializedName(value = "ThirdPartyAddress")
private List<String> thirdPartyAddress;
#SerializedName(value = "ConfidentialMonitors")
private List<String> confidentialMonitors;
#SerializedName(value = "ExternalBody")
private List<String> externalBody;
#SerializedName(value = "Controls")
private List<String> controls;
#SerializedName(value = "Witnesses")
private List<String> witnesses;
// getter/setter
....
}
Here is the CustomDeserializer class for Deserializing date properties
public class CustomDeserializer implements JsonDeserializer<Incident> {
#Override
public Incident deserialize(JsonElement json, Type type,
JsonDeserializationContext context) throws JsonParseException {
final JsonObject jsonObject = json.getAsJsonObject();
final Gson gson = new Gson();
// Parse the JsonElement tree here
final Incident incident = gson.fromJson(json, Incident.class);
// getting date properties as string from JsonElement and parse them into date object.
String dateReportedStr = jsonObject.get("DateReported").getAsJsonObject().get("$date").getAsString();
String dateComposedStr = jsonObject.get("DateComposed").getAsJsonObject().get("$date").getAsString();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
try {
// setting date properties in incident object
incident.setDateReported(df.parse(dateReportedStr));
incident.setDateComposed(df.parse(dateComposedStr));
} catch (ParseException e) {
e.printStackTrace();
}
return incident;
}
}
Finally Parse it
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Incident.class, new CustomDeserializer());
Gson gson = gsonBuilder.create();
Incident incident = gson.fromJson(Your_JSON_STR, Incident.class);
You have interesting date objects in your json string. Such as;
"DateReported": {
"$date": "2014-01-17T00:00:00.000Z"
}
Which causes a JsonParseException because of your Incident class:
com.google.gson.JsonParseException: The date should be a string value
For your Incident class, Dates at json value should be something like;
"DateReported": "2014-01-17T00:00:00.000Z"
If you don't have an option to change dates at json value, then you should define its custom date holder class:
public class CustomDateHolder {
#SerializedName("$date")
private Date date;
// Getters & Setters...
}
And change those date fields' type to CustomDateHolder;
public class Incident {
#SerializedName("_id")
private String _id;
private String docNo;
private String site_id;
private String company;
private String division;
private String department_id;
private CustomDateHolder dateReported;
private String briefDescription;
private String thirdPartyInvolvedYN;
private String supervisor;
private String classification;
private String status;
private String injuredPerson;
private String reportingPerson;
private CustomDateHolder occurDate;
private String occurTime;
// Getters & Setters...
}
Also modify your GsonBuilder a little bit:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE);
gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
Gson gson = gsonBuilder.create();
Related
I have a response that returns a json object in following format:
{
"playerId": "001",
"name": "michel",
"age": 21,
"nation": "USA",
"ratings": [
{
"type": "speed",
"score": "0121"
},
{
"type": "accuracy",
"score": "85"
}
],
"teaminfo": {
"teamName": "HON",
"isValid": "true"
}
}
and I have a Java Class as :
public class MyRider {
public String playerId;
public String name;
public int age;
public String speed;
public String accuracy;
public String teamName;
public String isValid;
//getter, setter...
}
I want to map the JSON object into Java object using GSON.
I tried using JsonDeserializationContext deserialize, and it returned null for the nested values in JSON.
Without custom deserializer
If you cannot change the JSON to return exactly what you want, I suggest you create classes to match it:
MyRider:
public class MyRider {
private String playerId;
private String name;
private int age;
private String nation;
private List<Rating> ratings;
private TeamInfo teaminfo;
// getters, setters, toString override
}
Rating:
public class Rating {
private String type;
private String score;
// getters, setters, toString override
}
TeamInfo:
private static class TeamInfo {
private String teamName;
private String isValid;
// getters, setters, toString override
}
Then simply deserialize as normal:
MyRider rider = gson.fromJson(json, MyRider.class);
If you need exactly the fields you've specified in MyRider in your question, consider a transformer class to map the full class above to your needs.
With custom deserializer
It's also possible to do this with a custom deserializer, but slightly pointless as GSON provides the normal mapping for you which you can then adapt.
Here is an example with a deserializer:
public class MyRiderDeserializer implements JsonDeserializer<MyRider> {
#Override
public MyRider deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context)
throws JsonParseException {
MyRider rider = new MyRider();
if(json.isJsonObject()) {
JsonObject riderObj = json.getAsJsonObject();
rider.setPlayerId(riderObj.get("playerId").getAsString());
rider.setName(riderObj.get("name").getAsString());
rider.setAge(riderObj.get("age").getAsInt());
JsonArray ratingsArray = riderObj.get("ratings").getAsJsonArray();
for(JsonElement ratingElem : ratingsArray) {
JsonObject ratingObj = ratingElem.getAsJsonObject();
String type = ratingObj.get("type").getAsString();
switch(type) {
case "speed":
rider.setSpeed(ratingObj.get("score").getAsString());
break;
case "accuracy":
rider.setAccuracy(ratingObj.get("score").getAsString());
break;
default:
break;
}
}
JsonObject teamInfo = riderObj.get("teaminfo").getAsJsonObject();
rider.setTeamName(teamInfo.get("teamName").getAsString());
rider.setIsValid(teamInfo.get("isValid").getAsString());
}
return rider;
}
}
Note this does not include any checks to validate whether the properties are actually there and is the simplest possible custom deserializer I could think of. To use it, you must register the type adapter at Gson creation time:
Gson gson = new GsonBuilder()
.registerTypeAdapter(MyRider.class, new MyRiderDeserializer())
.create();
MyRider myRider = gson.fromJson(reader, MyRider.class);
Getting empty java object while populating the following type of Json.
a.json:
------
{
"queries": [{
"query": {
"id": "q1",
"description": "Fire query to get the Auth token !!"
}
}
],
"executeQuery": ["q2", "q3"]
}
Query.java :
-----------
Note : #Data will take care of creating setter getter by Lombok library.
#Data
public class Query {
#Expose #SerializedName("id")
String id;
#Expose #SerializedName("description")
String description;
}
GRT.java :
----------
#Data
public class GRT{
#Expose #SerializedName("queries")
List<Query> queries ;
#Expose #SerializedName("executeQuery")
List<String> executeQuery;
}
Client call :
----------------------------------------------
private void readJson() throws IOException{
String fileName = "a.json";
// Get Gson object
Gson gson = newGsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
// read JSON file data as String
String fileData = new String(Files.readAllBytes(Paths.get(fileName)));
// parse json string to object
GenericRestTestDefinition grtDef = gson.fromJson(fileData, GenericRestTestDefinition.class);
System.out.println(grtDef.toString());
}
Printing the following :
GRT(queries=[Query(id=null, description=null)], executeQuery=[q2, q3])
Dont know why GRT-> Query Object is not getting populated ????
The proper JSON for this would look like this..
{
"queries":
[
{"id":"q1","description":"Fire query to get the Auth token"},
{"id":"q2","description":"Fire query to get the Auth token 2"}
]
}
public class Test {
public static void main(String[] args) throws Exception {
readJson();
}
private static void readJson() throws IOException {
String json ="{\"queries\":[{\"id\":\"q1\",\"description\":\"Fire query to get the Auth token\"}]}";
// Get Gson object
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
GRT grt = new GRT();
grt.setQueries(Arrays.asList( new Query[]{new Query("q1", "Fire query to get the Auth token")} ));
System.out.println(gson.toJson(grt));
// parse json string to object
GRT grtDef = gson.fromJson(json, new TypeToken<GRT>(){}.getType());
System.out.println(grtDef.queries.get(0));
}
}
If you can't change the json file format you can use this pattern:
#Data
public class GRT{
#Expose #SerializedName("queries")
private List<QueryWrapper> queries = new ArrayList<>();
public List<Query> getQueries() {
return queries.stream().map(it->it.query).collect(Collectors.toList());
}
#Expose #SerializedName("executeQuery")
List<String> executeQuery = new ArrayList<>();
}
#Data
public class QueryWrapper {
#Expose #SerializedName("query")
Query query;
}
#Data
public class Query {
public
#Expose #SerializedName("id")
String id;
#Expose #SerializedName("description")
String description;
}
{
"response": [
{
"id": "1",
"name": "xx"
},
{
"id": "2",
"name": "yy"
}
],
"errorMsg": "",
"code": 0
}
How to parse "response" alone using jackson parser. I am getting error as
Unrecognized field "errorMsg", not marked as ignorable.
My model class Response.java
public class Response {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
}
Your data model is a bit incomplete and this is what Jackson is pointing out.
To improve the situation you should map more fields.
public class Response {
#JsonProperty("id")
private Integer id;
#JsonProperty("name")
private String name;
// getter/setter...
}
public class Data {
#JsonProperty("response")
private List<Response> response;
#JsonProperty("errorMsg")
private String errorMsg;
#JsonProperty("code")
private int code;
// getter/setter...
}
You can either create a parent object and use #JsonIgnoreProperties. Alternatievly you could get the node and convert it to response object using ObjectMapper's convertValue() method like
try {
String json = "{\"response\":[{\"id\":\"1\",\"name\":\"xx\"},{\"id\":\"2\",\"name\":\"yy\"}],\"errorMsg\":\"\",\"code\":0}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
List<Response> responses = mapper.convertValue(node.findValues("response").get(0), new TypeReference<List<Response>>() {});
System.out.println(responses);
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
First of all I've seen this question, but I did not see the full answer to my question and this question was asked 2 years ago.
Introduction:
For example we have an JSON with such structure:
{
"name": "some_name",
"description": "some_description",
"price": 123,
"location": {
"latitude": 456987,
"longitude": 963258
}
}
I can use GSON library for auto parsing this JSON to my object's class.
For this I must create class describing JSON structure, like below:
public class CustomClassDescribingJSON {
private String name;
private String description;
private double price;
private Location location;
// Some getters and setters and other methods, fields, etc
public class Location {
private long latitude;
private long longitude;
}
}
And next I can auto parse JSON to object:
String json; // This object was obtained earlier.
CustomClassDescribingJSON object = new Gson().fromJson(json, CustomClassDescribingJSON.class);
I have a few ways for changing names of fields in my class (for writing more readable code or to follow language guidelines). One of them below:
public class CustomClassDescribingJSON {
#SerializedName("name")
private String mName;
#SerializedName("description")
private String mDescription;
#SerializedName("price")
private double mPrice;
#SerializedName("location")
private Location mLocation;
// Some getters and setters and other methods, fields, etc
public class Location {
#SerializedName("latitude")
private long mLatitude;
#SerializedName("longitude")
private long mLongitude;
}
}
Using same code like above for parsing JSON:
String json; // This object was obtained earlier.
CustomClassDescribingJSON object = new Gson().fromJson(json, CustomClassDescribingJSON.class);
But I could not find a possibility to change the structure of the class. For example, I would like to use next class for parsing the same JSON:
public class CustomClassDescribingJSON {
private String mName;
private String mDescription;
private double mPrice;
private long mLatitude;
private long mLongitude;
}
Questions:
Same as in the header: Is there way to associate arbitrary data structure with GSON parser?
Maybe there are another libraries to do what I want?
Would a custom GSON (de-)serializer help?
See https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization
Simply convert the JSON string into HashMap<String, Object> then populate any type of custom structure by simply iterating it or create a constructor in each custom object class as shown below to populate the fields.
class CustomClassDescribingJSON {
public CustomClassDescribingJSON(Map<String, Object> data) {
// initialize the instance member
}
}
Sample code:
Reader reader = new BufferedReader(new FileReader(new File("resources/json12.txt")));
Type type = new TypeToken<HashMap<String, Object>>() {}.getType();
HashMap<String, Object> data = new Gson().fromJson(reader, type);
System.out.println(new GsonBuilder().setPrettyPrinting().create().toJson(data));
output:
{
"price": 123.0,
"location": {
"latitude": 456987.0,
"longitude": 963258.0
},
"description": "some_description",
"name": "some_name"
}
This is my JSON:
{
"results": [
{
"user_id": "1",
"item_id": "18630",
"name": "Unnamed Item",
"price": "0",
"description": "",
"created_at": "2014-01-16 15:31:36",
"thumbnail": {
"image50": "http://www.example.com/adsa.jpg",
"image100": "hhttp://www.example.com/adsa.jpg"
},...
Am I doing the deserialization right?
public class ItemListModel {
private String user_id;
private String item_id;
private String name;
private String price;
private String category;
private ArrayList<ThumbnailResponse> thumbnail;
public ItemListModel(){}
// getters
}
public class ThumbnailResponse {
private String image50;
private String image100;
public ThumbnailResponse(){
}
//getters
}
I'm just confused, when do we use ArrayList, Array or List for array or object in the JSON file?
One more thing, do I need to make results as an array too if that's the case?
As you have given
"thumbnail": {
"image50": "http://www.example.com/adsa.jpg",
"image100": "hhttp://www.example.com/adsa.jpg"
}
is not a JsonArray. So you have no need to use ThumbnailResponse as an ArrayList into ItemListModel.
Your Model should be
public class ItemListModel {
private String user_id;
private String item_id;
private String name;
private String price;
private String category;
private ThumbnailResponse thumbnail; // Not array List
public ItemListModel(){}
// getters
}
And
One more thing, do I need to make results as an array too if that's
the case?
Your main data container should be contain ArrayList of ItemListModel. Like below
ArrayList<ItemListModel> results = new ArrayList<ItemListModel>();
[] in json -> array
{} in json -> object or map
in your case
// change
private ArrayList<ThumbnailResponse> thumbnail;
// to
private Map<String,String> thumbnail;
if you want it the way you declared your java object you need to provide a transformer (depends on the framework you are using)
List<ItemListModel > ItemListModel ;
try {
Type listType = new TypeToken<List<ItemListModel >>(){}.getType();
result= (List<ItemListModel >) gson.fromJson(result, listType);
} catch (Exception e) {
Log.e("Parsing exeption", e.getLocalizedMessage(), e);
}
this should work