Gson反序列化问题 – 如果类型参数是子类,则无法反序列化

huangapple go评论71阅读模式
英文:

Gson Deserializing Problem - If Type parameter is child class could not deserialize

问题

在变量 Generic<Parent> genericMaster 类中,我在 main() 中传递了一个 Child 对象。在序列化过程中,我得到了正确的输出。但是在反序列化时,Child 对象丢失了。有人能给予建议。

public class GenericSample {
    public static void main(String[] args) {
        Generic<Parent> generic = new Generic<Parent>();
        Child child = new Child();
        child.setName("I am child");
        generic.setT(child);

        Gson gson = new Gson();

        Master master = new Master();
        master.setId(2);
        master.setGeneric(generic);

        String valMaster = gson.toJson(master);
        System.out.println(valMaster);
        /*
         * 输出: {"id":2,"generic":{"t":{"name":"I am child"}}}
         */

        Master master2 = gson.fromJson(valMaster, Master.class);
        String valMaster2 = gson.toJson(master2);
        System.out.println(valMaster2);
        /*
         * Child 对象丢失
         * 输出: {"id":2,"generic":{"t":{}}}
         */
    }

    static class Master {
        private int id;
        private Generic<Parent> generic;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public Generic<Parent> getGeneric() {
            return generic;
        }

        public void setGeneric(Generic<Parent> generic) {
            this.generic = generic;
        }
    }

    static class Generic<T> {
        T t;

        public T getT() {
            return t;
        }

        public void setT(T t) {
            this.t = t;
        }
    }

    static class Parent {
        private String type;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

    static class Child extends Parent {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
英文:

In Master class for the variable Generic<Parent> generic i am passing a Child Object in main(). During serializing i am getting correct output. But while deserializing Child Object is missing. Could anyone give suggesstions.

public class GenericSample {
public static void main(String[] args) {
Generic<Parent> generic = new Generic<Parent>();
Child child = new Child();
child.setName("I am child");
generic.setT(child);
Gson gson = new Gson();
Master master = new Master();
master.setId(2);
master.setGeneric(generic);
String valMaster = gson.toJson(master);
System.out.println(valMaster);
/*
* Output: {"id":2,"generic":{"t":{"name":"I am child"}}}
*/
Master master2 = gson.fromJson(valMaster, Master.class);
String valMaster2 = gson.toJson(master2);
System.out.println(valMaster2);
/*
* Child Object is missing
* Output: {"id":2,"generic":{"t":{}}}
*/
}
static class Master {
private int id;
private Generic<Parent> generic;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Generic<Parent> getGeneric() {
return generic;
}
public void setGeneric(Generic<Parent> generic) {
this.generic = generic;
}
}
static class Generic<T> {
T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
static class Parent {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
static class Child extends Parent {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

答案1

得分: 1

问题

Gson尝试将泛型值反序列化为Parent,而不是Child。由于typenull,您可以看到反序列化后的对象中没有数据,看起来是{}。如果您添加child.setType("type");,则输出变为:

  • valMaster1{"id":2,"generic":{"t":{"name":"I am child","type":"type"}}}
  • valMaster2{"id":2,"generic":{"t":{"type":"type"}}}

然而,字段name不在Parent类中,而在Child类中,Gson根本不知道它是Parent的哪个子类(如果是的话),并且完全忽略该值,这是正确的行为。

解决方案

我基本上找到了两种选择(为了简洁起见,我使用了全参构造函数):

  1. 将上界泛型类型参数提升到Master类,并在反序列化时使用com.google.gson.reflect.TypeTokenjava.lang.reflect.Type指定特定的Child类型:

    static class Master<T extends Parent> {
        private int id;
        private Generic<T> generic;
    
        /* 获取器,设置器和全参构造函数 */
    }
    
    Child child = new Child("I am child");
    Generic<Parent> generic = new Generic<>(child);
    Master<Parent> master = new Master<>(2, generic);
    
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);                           
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
    Type type = new TypeToken<Master<Child>>() {}.getType();
    Master<Child> master2 = gson.fromJson(valMaster, type);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
  2. Master类内部硬编码特定的泛型类型Generic<Child>。反序列化变得更容易,但设计较不灵活:

    static class Master {
        private int id;
        private Generic<Child> generic;
    
        /* 获取器,设置器和全参构造函数 */
    }
    
    Child child = new Child("I am child");
    Generic<Child> generic = new Generic<>(child);
    Master master = new Master(2, generic);
    
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
    Master master2 = gson.fromJson(valMaster, Master.class);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {"id":2,"generic":{"t":{"name":"I am child"}}}
    
英文:

Problem

Gson tries to deserialize the generic value into Parent, not Child. Since there is type as null, you can see no data in the object deserialized which appears as {}. If you add child.setType(&quot;type&quot;); then the outputs become:

  • valMaster1: {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;name&quot;:&quot;I am child&quot;,&quot;type&quot;:&quot;type&quot;}}}
  • valMaster2: {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;type&quot;:&quot;type&quot;}}}

However, the field name is not present in the Parent class but the Child class and Gson simply has no idea what subclass of Parent it is (if so) and completely ignores the value, which is a correct behavior.

Solution

I find basically two choices (I use all-args constructor for sake of brevity):

  1. Elevate the upper-bounded generic type parameter to the Master class and specify the particular Child type at the point of deserialization using com.google.gson.reflect.TypeToken and java.lang.reflect.Type:

    static class Master&lt;T extends Parent&gt; {
    private int id;
    private Generic&lt;T&gt; generic;
    /* getters, setters and all-args constructor */
    }
    
    Child child = new Child(&quot;I am child&quot;);
    Generic&lt;Parent&gt; generic = new Generic&lt;&gt;(child);
    Master&lt;Parent&gt; master = new Master&lt;&gt;(2, generic);
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);                           
    // {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;name&quot;:&quot;I am child&quot;}}}
    Type type = new TypeToken&lt;Master&lt;Child&gt;&gt;() {}.getType();
    Master&lt;Child&gt; master2 = gson.fromJson(valMaster, type);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;name&quot;:&quot;I am child&quot;}}}
    
  2. Hardcode the particular generic type Generic&lt;Child&gt; inside the Master class. The deserialization gets the way easier, yet the design is less flexible:

    static class Master {
    private int id;
    private Generic&lt;Child&gt; generic;
    /* getters, setters and all-args constructor */
    }
    
    Child child = new Child(&quot;I am child&quot;);
    Generic&lt;Child&gt; generic = new Generic&lt;&gt;(child);
    Master master = new Master(2, generic);
    Gson gson = new Gson();
    String valMaster = gson.toJson(master);
    System.out.println(valMaster);
    // {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;name&quot;:&quot;I am child&quot;}}}
    Master master2 = gson.fromJson(valMaster, Master.class);
    String valMaster2 = gson.toJson(master2);
    System.out.println(valMaster2);
    // {&quot;id&quot;:2,&quot;generic&quot;:{&quot;t&quot;:{&quot;name&quot;:&quot;I am child&quot;}}}
    

huangapple
  • 本文由 发表于 2020年10月20日 15:31:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/64440488.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定