-1

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:

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)
    ....
MTCoster
  • 5,868
  • 3
  • 28
  • 49
Spasitel
  • 169
  • 7
  • 4
    Can you print the exception retrieved? – juanlumn Jan 29 '19 at 12:49
  • 2
    Without seeing the exception, my guess is the problem is in the cycle in your object structure: `MeetingRoom -> Reservation -> MeetingRoom`. You need to break the structure somehow. For example instead of reference to the MeetingRoom in reservation, you may want to serialize only the code of the MeetingRoom. – NeplatnyUdaj Jan 29 '19 at 12:58
  • I see, so I cant use gson for that, as long as I dont want to change they way Reservation class is implemented. – Spasitel Jan 29 '19 at 13:08
  • You can add some Gson annotations on the fields or if you cannot modify the sources(if it comes from a library for example), you can implement a serializer for the Reservation class. https://futurestud.io/tutorials/gson-advanced-custom-serialization-part-1 – NeplatnyUdaj Jan 29 '19 at 13:28

2 Answers2

0

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();
Michael
  • 41,989
  • 11
  • 82
  • 128
0

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

         ...