Gson - Setting object reference on-the-fly using InstanceCreator

馋奶兔 提交于 2019-11-29 08:13:46

You should definitely use GraphAdapterBuilder.

As you said in the comment under @Braj 's answer,

workshift is set transient for a reason so that it won't serialize this object when serializing visit object. If its not marked as transient then the serialization falls into stack overflow exception - by creating an unstoppable loop

This has a simple solution.

Workshift.java

public class Workshift {
    private final transient Context context;
    private final Visit visit;
    //for testing
    private String workshift_description;

    public Workshift(Context context,String id) {
        this.workshift_description=id;
        this.context = context;
        this.visit = new Visit(this);

    }
    public String getId() {
        return workshift_description;
    }

    public void setId(String id) {
        this.workshift_description = id;
    }
    public String toString() {
        return "[Workshift element => { WD: "+this.workshift_description+", VD : "+this.visit.getVisit_description()+"}";
    }
}

Visit.java

public class Visit {

    private final /* transient  */ Workshift workshift;

    public Visit(Workshift ws) {
        this.workshift = ws;

    }
    public String getVisit_description() {
        return "visit containing  "+ workshift.getId();
    }

}

The trick resides here:

GsonBuilder gsonBuilder = new GsonBuilder();
        new GraphAdapterBuilder()
        .addType(Visit.class)
        .addType(Workshift.class)
        .registerOn(gsonBuilder);

Putting all together,

public static void main(String[] args) {

        Workshift[] workshifts = new Workshift[10];
        for (int i = 0; i < workshifts.length; i++) {
            //Replace Context(i) for the real one
            workshifts[i] = new Workshift(new Context(i), "Workshift#"
                    + i);
        }
        System.out.println("Original Workshifts array:");
        for (int i = 0; i < workshifts.length; i++) {
            System.out.println(workshifts[i]);
        }
        System.out.println("===================================");

        GsonBuilder gsonBuilder = new GsonBuilder();
        new GraphAdapterBuilder()
        .addType(Visit.class)
        .addType(Workshift.class)
        .registerOn(gsonBuilder);

        Gson gson = gsonBuilder.setPrettyPrinting().create();
        String serialized = gson.toJson(workshifts);
        // System.out.println(serialized);
        Workshift[] w_array = gson.fromJson(serialized, Workshift[].class);
        // System.out.println(gson.toJson(w_array));

        System.out.println("Des-serialized Workshifts array:");
        for (int i = 0; i < w_array.length; i++) {
            System.out.println(w_array[i]);
        }
        System.out.println("===================================");

Output:

Original Workshifts array:
[Workshift element => { WD: Workshift#0, VD : visit containing  Workshift#0}
[Workshift element => { WD: Workshift#1, VD : visit containing  Workshift#1}
[Workshift element => { WD: Workshift#2, VD : visit containing  Workshift#2}
[Workshift element => { WD: Workshift#3, VD : visit containing  Workshift#3}
[Workshift element => { WD: Workshift#4, VD : visit containing  Workshift#4}
[Workshift element => { WD: Workshift#5, VD : visit containing  Workshift#5}
[Workshift element => { WD: Workshift#6, VD : visit containing  Workshift#6}
[Workshift element => { WD: Workshift#7, VD : visit containing  Workshift#7}
[Workshift element => { WD: Workshift#8, VD : visit containing  Workshift#8}
[Workshift element => { WD: Workshift#9, VD : visit containing  Workshift#9}
===================================
Des-serialized Workshifts array:
[Workshift element => { WD: Workshift#0, VD : visit containing  Workshift#0}
[Workshift element => { WD: Workshift#1, VD : visit containing  Workshift#1}
[Workshift element => { WD: Workshift#2, VD : visit containing  Workshift#2}
[Workshift element => { WD: Workshift#3, VD : visit containing  Workshift#3}
[Workshift element => { WD: Workshift#4, VD : visit containing  Workshift#4}
[Workshift element => { WD: Workshift#5, VD : visit containing  Workshift#5}
[Workshift element => { WD: Workshift#6, VD : visit containing  Workshift#6}
[Workshift element => { WD: Workshift#7, VD : visit containing  Workshift#7}
[Workshift element => { WD: Workshift#8, VD : visit containing  Workshift#8}
[Workshift element => { WD: Workshift#9, VD : visit containing  Workshift#9}
===================================

There's no StackOverflow error.

if you un-comment the line

// System.out.println(serialized);

The output would be like this:

[
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#0"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#1"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#2"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#3"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#4"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#5"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#6"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#7"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#8"
    },
    "0x2": {
      "workshift": "0x1"
    }
  },
  {
    "0x1": {
      "visit": "0x2",
      "workshift_description": "Workshift#9"
    },
    "0x2": {
      "workshift": "0x1"
    }
  }

]

That's because Gson is replacing your references, in order to avoid that stack overflow exception. It's like emulating pointers

Hope it helps.

Note: Remember to copy the files GraphAdapterBuilder.java and change the line

private final ConstructorConstructor constructorConstructor = new ConstructorConstructor();

with

private final ConstructorConstructor constructorConstructor = new ConstructorConstructor(instanceCreators);

It won't compile otherwise. Maybe it's fixed right now.

Problem:

Currently, the workshift field inside Visit turns out to be null when deserialized.

Solution:

workshift field is a transient member in Visit class and transient members won't be serialized that's why you are getting null value when deserialized.

To solve this problem you have to set the reference of workshift in the visit class manually via calling its setter method after getting workshift object when deserialized.

when deserialized you have reference of both the object workshift and visit. Just need to pass the reference of workshift to visit will solve it.

Visit.java:

public class Visit {
    private final transient Workshift workshift;

    public Visit() {

    }

    public Workshift getWorkshift() {
        return workshift;
    }

    public void setWorkshift(Workshift workshift) {
        this.workshift = workshift;
    }

}

Use JsonDeserializer to set the reference of workshift into visit class.

Sample Code:

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;

    public class GSON {

        /**
         * @param args
         * @throws IOException
         * @throws ClassNotFoundException
         */
        public static void main(String[] args) throws Exception {
            // serialize
            Gson gson = new Gson();
            String json = gson.toJson(new Workshift());
            System.out.println("Workshift JSON:" + json);

            // deserialize
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(Workshift.class, new WorkshiftDeserializer());

            Workshift workshift = builder.create().fromJson(json, Workshift.class);
            System.out.println("Reference of Workshift from Visit:"
                    + workshift.getVisit().getWorkshift());

        }

    }

    class Workshift implements Serializable {
        private Visit visit;

        public Workshift() {
            this.visit = new Visit(this);
        }

        public Visit getVisit() {
            return visit;
        }

        public void setVisit(Visit visit) {
            this.visit = visit;
        }

    }

    class Visit implements Serializable {
        private transient Workshift workshift;

        public Visit() {

        }

        public Visit(Workshift ws) {
            this.workshift = ws;
        }

        public Workshift getWorkshift() {
            return workshift;
        }

        public void setWorkshift(Workshift workshift) {
            this.workshift = workshift;
        }

    }


    class WorkshiftDeserializer implements JsonDeserializer<Workshift> {
        public Workshift deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                throws JsonParseException {

            Gson gson = new Gson();

            Workshift workshift = gson.fromJson(json, Workshift.class);
            workshift.getVisit().setWorkshift(workshift);
            return workshift;
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!