How do I deserialize an array of objects using gson and JsonWriter - java

I have the following classes:
public class MeetingCenter {
private String name;
private List<MeetingRoom> meetingRoomList;
}
public class MeetingRoom {
private MeetingCenter meetingCenter;
private String code;
private String name;
private List<Reservation> reservationList;
}
public class Reservation {
private MeetingRoom meetingRoom;
private String owner;
}
And I want to create a JSON with following schema:
This method throws me an exception on the line where I call the toJson() method:
private static void exportToJson(List<MeetingCenter> mcs) throws IOException {
Gson gson = new Gson();
String data = gson.toJson(mcs);
JsonWriter writer = new JsonWriter(new FileWriter("export.json"));
writer.setIndent(" "); // set indent
writer.beginObject(); // document start
writer.name("schema").value("PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0");
writer.name("uri").value("ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE");
writer.name("data").value(data);
writer.endObject(); // document end
writer.close();
}
The exception:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.StringBuffer.append(StringBuffer.java:380)
at java.io.StringWriter.write(StringWriter.java:77)
at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:614)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.beginArray(JsonWriter.java:287)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:95)
....

Your objects all have references to their parents.
GSON takes looks at a MeetingCenter then tries to serialize its child MeetingRooms. The MeetingRooms have reference back to the MeetingCenter, so GSON goes around and around in circles until you get a stack overflow.
To fix this, you can make sure to only expose the children and not the parents. There are plenty of questions showing this already. See Java Gson Exclude fields during serialization.
For example, your Reservation might look like this:
class Reservation {
MeetingRoom meetingRoom;
#Expose
String owner;
}
I'll leave the rest up to you.
Also, you have a method call to writer.endArray() when you have not started an array. Remove that line.
writer.beginObject(); // document start
writer.name("schema").value("PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0");
writer.name("uri").value("ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE");
writer.name("data").value(data);
//writer.endArray(); removed
writer.endObject(); // document end
writer.close();

The #Expose is the solution to avoid the stackoverflow exception but the statement
writer.name("data").value(data);
is not valide because the data will be enriched with escape char. For exampla you can have in the data field
"data": "{\"name\": \"center 1\" ... }"
so there can will be problems in the deserialize phase.
My implementation proposes a Container class for the MeetingCenter class where the schema and the URI can be configured.
/** Container class configures the schema and URI */
public class Container {
#Expose
private String schema;
#Expose
private String uri;
#Expose
private List<MeetingCenter> data;
}
public class Reservation {
private MeetingRoom meetingRoom;
#Expose
private String owner;
}
public class MeetingRoom {
private MeetingCenter meetingCenter;
#Expose
private String code;
#Expose
private String name;
#Expose
private List<Reservation> reservationList;
}
public class MeetingCenter {
#Expose
private String name;
#Expose
private List<MeetingRoom> meetingRoomList;
}
public class Main {
public static void main(String[] args){
Container container = meetingCenterInitialization();
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
// it is necessary to avoid stackoverflow
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
String jsonString = gson.toJson(container);
System.out.println(jsonString);
Container container1 = gson.fromJson(jsonString, Container.class);
System.out.println("\n\n\n\n" + container1.getData().get(0).getName());
}
}
The output of the main method is
{
"schema": "PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0",
"uri": "ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE",
"data": [
{
"name": "center name",
"meetingRoomList": [
{
"code": "room 1",
...

Related

Gson is not getting populated in Java List<Object>

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;
}

Java object not populated from json request for inner class

Have searched in different sites but couldn't find correct answer, hence posting this request though it could possible duplicates.sorry for that.
I am sending the below json request to my back-end service and converting to java object for processing. I can see the request body passed to my service but when i convert from json to java object , values are not populating
{
"data":{
"username":"martin",
"customerId":1234567890,
"firstName":"john",
"lastName":"smith",
"password":"p#ssrr0rd##12",
"email":"john.smith#gmail.com",
"contactNumber":"0342323443",
"department":"sports",
"location":"texas",
"status":"unlocked",
"OrderConfigs":[
{
"vpnId":"N4234554R",
"serviceId":"connectNow",
"serviceType":"WRLIP",
"ipAddress":"10.101.10.3",
"fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
"timeout":1800,
"mapId":"test_map"
}
]
}
}
My Parser class have something like,
JSONObject requestJSON = new JSONObject(requestBody).getJSONObject("data");
ObjectMapper mapper = new ObjectMapper();
final String jsonData = requestJSON.toString();
OrderDTO mappedObject= mapper.readValue(jsonData , OrderDTO .class);
// I can see value coming from front-end but not populating in the mappedObject
My OrderDTO.java
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true,value = {"hibernateLazyInitializer", "handler", "created"})
public class OrderDTO {
private String username;
private long customerId;
private String source;
private String firstName;
private String lastName;
private String email;
private String contactNumber;
private String password;
private String department;
private String location;
private String status;
private List<OrderConfig> OrderConfigs;
#JsonInclude(value = Include.NON_NULL)
public class OrderConfig {
private String vpnId;
private String serviceId;
private String serviceType;
private String ipAddress;
private String mapId;
private String[] fRoutes;
private Map<String, Object> attributes;
private SubConfig subConfig;
private String routeFlag;
getter/setters
.....
}
all setter/getter
}
Not sure what I'm missing here. Is this right way to do?
If your are trying to use inner class, correct way to use is to declare it static for Jackson to work with inner classes.
For reference check this
code changes made are
#JsonInclude(value = Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
static class OrderConfig {
Make sure that your json tag names match with variable names of java object
Ex : "fRoute":[
"10.255.253.0/30",
" 10.255.254.0/30"
],
private String[] fRoutes;
OrderConfigs fields will not be initialized, just modify your bean as
#JsonProperty("OrderConfigs")
private List<OrderConfig> orderConfigs;
// setter and getter as setOrderConfigs / getOrderConfigs
See my answer here. (same issue)

toJson() of GSON library returning wrong json

I am using GSON library to pass json to server as header.
But it is not generating my expected json.
My Pojo class "TestRequest.java" is like:
public class TestRequest {
private String mobileNumber;
public TestRequest(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
}
Here is my code to call the GSON class to make json:
Gson gson = new Gson();
TestRequest tt = new TestRequest("+8801913000000");
String json = gson.toJson(tt);
My expected json is :
{"mobileNumber":"+8801913000000"}
But I am getting:
{"aIf":"+8801913000000"}
Note: This code was working perfectly 2 days before.
Try to change your pojo class like
public class TestRequest implements Serializable {
#SerializedName("mobileNumber")
private String mobileNumber;
public TestRequest(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
public String getMobileNumber() {
return mobileNumber;
}
public void setMobileNumber(String mobileNumber) {
this.mobileNumber = mobileNumber;
}
}
Let me know if not work

Jackson - Deserialize empty String Member to null

I like to deserialize with Jackson an empty String member ("") to null. The Deserialization Feature "ACCEPT_EMPTY_STRING_AS_NULL_OBJECT" can for this unfortunately not be used (see link).
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
public class Supplier {
private Integer id;
private String name;
private String image;
private String link;
private String description;
}
So after deserialization of the following JSON String the string members "link" and "image" should be null and not "".
{"id":37,"name":"Life","image":"","link":"","description":null}
I am looking for a way to write an own deserializer which can be used for String members of a POJO. Is there a way to achieve this? I am using faster Jackson 2.6.0.
The custom deserializer can be done as follows in Jackson 2.6.0.
public class SupplierDeserializer extends JsonDeserializer<Supplier> {
#Override
public Supplier deserialize(JsonParser jp, DeserializationContext context) throws IOException, JsonProcessingException {
Supplier sup = new Supplier();
JsonNode node = jp.readValueAsTree();
sup.setId(node.get("id").asInt());
sup.setDescription(node.get("description").asText());
String image = node.get("image").asText();
if("".equals(image)) {
image = null;
}
sup.setImage(image);
String link = node.get("link").asText();
if("".equals(link)) {
link = null;
}
sup.setLink(link);
sup.setName(node.get("name").asText());
return sup;
}
}
Register the custom deserialiser with the Supplier class
#JsonDeserialize(using = SupplierDeserializer.class)
public class Supplier {
private Integer id;
private String name;
private String image;
private String link;
private String description;
// getters and setters
}
Call the ObjectMapper class to parse the JSON data
String jsonData = "{\"id\":37,\"name\":\"Life\",\"image\":\"\",\"link\":\"\",\"description\":null}";
Supplier sup = new ObjectMapper().readValue(jsonData, Supplier.class);

Is there way to associate arbitrary data structure with GSON parser?

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"
}

Categories