首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当对象的基本列表时,GSON不包括区分器字段

当对象的基本列表时,GSON不包括区分器字段
EN

Stack Overflow用户
提问于 2017-06-01 13:25:37
回答 1查看 1.1K关注 0票数 1

在当前的项目中,我遇到了GSON多态对象的奇怪情况。情况如下:我有两个不同的抽象基类,它们有两个不同的用例:

  • 第一个类本身包含在一个列表中,并且
  • 第二个类也包含在一个列表中,但是这个列表是一个更大的父对象的一部分。

人为类的简化版本(为简洁起见省略了构造函数和访问器;已定义但为说明而注释掉的区分器字段):

代码语言:javascript
复制
public abstract class ClassNumeric {
    //private String numericType;
}

public class ClassOne extends ClassNumeric {
   private String hex;
}

public class ClassTwo extends ClassNumeric {
    private Integer whole;
    private Integer fraction;
}

public abstract class ClassAlphabetic {
    //private String alphabeticType;
}

public class ClassAlpha extends ClassAlphabetic {
    private String name;
}

public class ClassBravo extends ClassAlphabetic {
    private Integer age;
    private Integer numberOfMarbles;
}

public class Container {
    private String group;
    private List<ClassAlphabetic> alphabetics;
}

适配器工厂及其在GSON中的注册:

代码语言:javascript
复制
public RuntimeTypeAdapterFactory<ClassNumeric> numericTypeFactory = RuntimeTypeAdapterFactory
    .of(ClassNumeric.class, "numericType")
    .registerSubtype(ClassOne.class)
    .registerSubtype(ClassTwo.class);

public RuntimeTypeAdapterFactory<ClassAlphabetic> alphabeticTypeFactory = RuntimeTypeAdapterFactory
    .of(ClassAlphabetic.class, "alphabeticType")
    .registerSubtype(ClassAlpha.class)
    .registerSubtype(ClassBravo.class);

public final Gson gson = new GsonBuilder()
    .setPrettyPrinting()
    .disableHtmlEscaping()
    .registerTypeAdapterFactory(numericTypeFactory)
    .registerTypeAdapterFactory(alphabeticTypeFactory)
    .create();

根据到目前为止所读到的内容,我不必(实际上也不应该)声明基类中的区分器字段,因为在JSON被序列化和反序列化时,GSON在内部处理这些字段。

下面是如何使用这些方法的示例:

代码语言:javascript
复制
ClassOne c1 = ClassOne.builder().hex("EF8A").build();
ClassTwo c2 = ClassTwo.builder().whole(1).fraction(3).build();
List<ClassNumeric> numerics = Arrays.asList(c1, c2);   // List of child objects
log.debug("Numerics: " + gson.toJson(numerics));

ClassAlpha ca = ClassAlpha.builder().name("Fred").build(); 
ClassBravo cb = ClassBravo.builder().age(5).numberOfMarbles(42).build();
List<ClassAlphabetic> alphas = Arrays.asList(ca, cb);
Container container = Container.builder().group("Test Group 1").alphabetics(alpha).build();  // List of objects field on larger object
log.debug("Alphas (container): " + gson.toJson(container));

我遇到的问题是,ClassAlphabetic对象工作正常(判别器字段在JSON中),而ClassNumeric对象不工作(缺少区分器字段)。样本输出:

代码语言:javascript
复制
09:12:17.910 [main] DEBUG c.s.s.s.s.GSONPolymorphismTest - Numerics: [
    {
        "hex": "EF8A"
    },
    {
        "whole": 1,
        "fraction": 3
    }
]
09:12:17.926 [main] DEBUG c.s.s.s.s.GSONPolymorphismTest - Alphas (container): {
    "group": "Test Group 1",
    "alphabetics": [
        {
            "alphabeticType": "ClassAlpha",
            "name": "Fred"
        },
        {
            "alphabeticType": "ClassBravo",
            "age": 5,
            "numberOfMarbles": 42
        }
    ]
}

我在这里错过了什么?这些在本质上是用相同的方式定义和设置的,但是一个用例在另一个用例不工作的情况下工作。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-06-01 22:47:23

这是因为泛型在Java中的工作方式。简而言之,特定的泛型类实例没有任何类型参数化信息作为实例的一部分。但是,类型参数可以存储在字段类型、方法返回类型、方法参数、继承的超类(例如extends ArrayList<Integer>)、自定义参数化类型信息实例等中。局部变量,比如numerics,在编译期间保存类型参数,并且在您和编译器的头脑中存在--因为类型擦除。因此,它就像运行时中的原始List。与numerics类似,alphas没有任何运行时参数化,但与局部变量不同,Container.alphabetics字段具有保存在运行时中的类型信息--字段可以提供完整的类型信息。Gson使用它来确定要应用哪种(反)序列化策略。类似地,当没有提供额外的类型参数信息时,Gson使用默认策略(比如局部变量)。如前所述,您还可以创建一个自定义参数化类型ParameterizedType,以提供原始类型及其类型参数信息。怎么能帮上忙?如果您仔细观察一下Gson toJson重载,您会发现它的重载接受中有一个是额外的参数(我已经选择了最简单的一个)。这可以看作是Gson的一种提示,可以告诉它传递的实例的确切类型(不一定匹配,但在大多数情况下应该匹配)。因此,为了使其工作,告诉Gson您的numerics List类型参数化:

代码语言:javascript
复制
// "Canonical" way: TypeToken analyzes its superclass parameterization and returns its via the getType method
private static final Type classNumericListType = new TypeToken<List<ClassNumeric>>() {
}.getType()));

System.out.println("Numerics: " + gson.toJson(numerics, classNumericListType);

或者,如果你可以使用Gson 2.8.0+,

代码语言:javascript
复制
private static final Type classNumericListType = TypeToken.getParameterized(List.class, ClassNumeric.class);

System.out.println("Numerics: " + gson.toJson(numerics, classNumericListType);

或者简单地创建自己的ParameterizedType实现。当类型在编译时已知时,我将使用类型标记;如果类型仅在运行时已知,则使用TypeToken.getParameterized。这样,传递类型实例将触发RuntimeTypeAdapterFactory机制(现在它是ClassNumeric,而不是原始Object),因此输出如下:

代码语言:javascript
复制
Numerics: [
  {
    "numericType": "N1",
    "hex": "EF8A"
  },
  {
    "numericType": "N2",
    "whole": 1,
    "fraction": 3
  }
]
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44308814

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档