Gson – 使用InstanceCreator即时设置对象引用

我遇到了一个问题,我需要在反序列化期间使用GsonInstanceCreator设置我的对象的引用被解析为它的子对象。

为了描述问题,以下是类结构的简单表示。

 public class Workshift { private final transient Context context; private final Visit visit; public Workshift(Context context) { this.context = context; this.visit = new Visit(this); } } public class Visit { private final transient Workshift workshift; public Visit(Workshift ws) { this.workshift = ws; } } 

通过这种结构,我可以通过向我的GsonBuilder提供InstanceCreator来设置GsonBuilder ,例如:

 Gson gson = new GsonBuilder() .registerTypeAdapter(Workshift.class, new InstanceCreator() { @Override public Workshift createInstance(Type type) { return new Workshift(context); } }) .create(); 

我知道,我可以在我的GsonBuilder添加额外的InstanceCreator ,但我不确定如何提供我的Workshift对象的引用, Workshift对象正在被解析(即时)到Visit对象?

任何帮助,将不胜感激!

你绝对应该使用GraphAdapterBuilder 。

正如你在@Braj的回答中所说的那样,

由于某种原因, workshift设置为瞬态,因此在序列化访问对象时它不会序列化此对象。 如果它没有被标记为瞬态,那么序列化就会陷入堆栈溢出exception – 通过创建一个不可阻挡的循环

这有一个简单的解决方案。

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

诀窍在于:

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

全部放在一起,

 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("==================================="); 

输出:

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

没有StackOverflow错误。

如果你取消评论该行

 // System.out.println(serialized); 

输出将是这样的:

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

]

那是因为Gson正在替换你的引用,以避免堆栈溢出exception。 这就像模仿指针一样

希望能帮助到你。

注意:请记住复制文件GraphAdapterBuilder.java并更改该行

 private final ConstructorConstructor constructorConstructor = new ConstructorConstructor(); 

private final ConstructorConstructor constructorConstructor = new ConstructorConstructor(instanceCreators);

它不会编译。 也许现在已经修好了。

问题:

目前,访问中的workshift字段在反序列化时结果为null。

解:

workshift字段是Visit类中的临时成员,并且瞬态成员不会被序列化,这就是反序列化时获取空值的原因。

要解决此问题,您必须在反序列化后获取workshift对象后通过调用其setter方法手动设置访问类中的workshift引用。

反序列化时,您既可以参考对象的工作class次也可以访问。 只需要传递workshift的引用就可以解决它。

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

使用JsonDeserializer将workshift的引用设置为visit类。

示例代码:

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