如何使用 Gson 序列化,在使用通用通配符时给出不同的结果?

请考虑以下示例:


static class BaseBean { String baseField = "base"; }

static class ChildBean extends BaseBean { String childField = "child"; }


static class BaseBeanHolder {

    List <? extends BaseBean> beans;


    public BaseBeanHolder(List<? extends BaseBean> beans) { this.beans = beans; }

}


static class ChildBeanHolder {

    List <ChildBean> beans;


    public ChildBeanHolder(List<ChildBean> beans) { this.beans = beans; }

}


@Test

public void mcve() {

    BaseBeanHolder baseHolder = new BaseBeanHolder(singletonList(new ChildBean()));

    System.out.println(new Gson().toJson(baseHolder));


    ChildBeanHolder childHolder = new ChildBeanHolder(singletonList(new ChildBean()));

    System.out.println(new Gson().toJson(childHolder));

}

它打印:


{“beans”:[{“baseField”:“base”}]}


{“beans”:[{“childField”:“child”,“baseField”:“base”}]}


因此,尽管这两个列表都包含子对象,但只有第二个持有者会导致子字段序列化为 JSON。


我看到了其他问题,比如这里,但我想知道是否有合理的解决方法来实现我想要的。


换句话说:有没有办法让这样一个“holder”类接受BaseBeans或ChildBeans(这样做),并且在将Gson的实例序列化为JSON字符串时也为我提供了正确的结果?<? extends BaseBean>


(注意:我不能使用特定类型的适配器,因为我无法控制实际的Gson实例来自哪里以及如何在我们的环境中配置它)


红糖糍粑
浏览 144回答 3
3回答

慕斯王

通常,集合实现从集合字段声明“take”类型 - 而不是从 //etc 上的给定项。我们需要编写自定义序列化器,为每个项目查找序列化程序并使用它。实现简单:ListSetclass TypeAwareListJsonSeserializer implements JsonSerializer<List<?>> {&nbsp; &nbsp; @Override&nbsp; &nbsp; public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {&nbsp; &nbsp; &nbsp; &nbsp; if (src == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return JsonNull.INSTANCE;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; JsonArray array = new JsonArray();&nbsp; &nbsp; &nbsp; &nbsp; for (Object item : src) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; JsonElement jsonElement = context.serialize(item, item.getClass());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; array.add(jsonElement);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return array;&nbsp; &nbsp; }}这就是我们如何使用它:import com.google.gson.Gson;import com.google.gson.GsonBuilder;import com.google.gson.JsonArray;import com.google.gson.JsonElement;import com.google.gson.JsonNull;import com.google.gson.JsonSerializationContext;import com.google.gson.JsonSerializer;import com.google.gson.annotations.JsonAdapter;import java.lang.reflect.Type;import java.util.Arrays;import java.util.List;public class GsonApp {&nbsp; &nbsp; public static void main(String[] args) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; List<BaseBean> children = Arrays.asList(new BaseBean(), new ChildBean(), new ChildBean2());&nbsp; &nbsp; &nbsp; &nbsp; BaseBeanHolder baseHolder = new BaseBeanHolder(children);&nbsp; &nbsp; &nbsp; &nbsp; Gson gson = new GsonBuilder()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setPrettyPrinting()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .create();&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(gson.toJson(baseHolder));&nbsp; &nbsp; }}class BaseBean {&nbsp; &nbsp; String baseField = "base";}class ChildBean extends BaseBean {&nbsp; &nbsp; String childField = "child";}class ChildBean2 extends BaseBean {&nbsp; &nbsp; int bean2Int = 356;}class BaseBeanHolder {&nbsp; &nbsp; @JsonAdapter(TypeAwareListJsonSeserializer.class)&nbsp; &nbsp; private List<? extends BaseBean> beans;&nbsp; &nbsp; // getters, setters, toString}以上代码打印:{&nbsp; "beans": [&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "baseField": "base"&nbsp; &nbsp; },&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "childField": "child",&nbsp; &nbsp; &nbsp; "baseField": "base"&nbsp; &nbsp; },&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "bean2Int": 356,&nbsp; &nbsp; &nbsp; "baseField": "base"&nbsp; &nbsp; }&nbsp; ]}编辑在序列化过程中,我们会丢失有关反序列化过程中所需的类型的信息。我开发了简单的类型信息,这些信息将在序列化期间存储并用于反序列化。它可能如下所示:class TypeAwareListJsonAdapter implements JsonSerializer<List<?>>, JsonDeserializer<List<?>> {&nbsp; &nbsp; private final String typeProperty = "@type";&nbsp; &nbsp; @Override&nbsp; &nbsp; public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {&nbsp; &nbsp; &nbsp; &nbsp; if (src == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return JsonNull.INSTANCE;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; JsonArray array = new JsonArray();&nbsp; &nbsp; &nbsp; &nbsp; for (Object item : src) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; JsonObject jsonElement = (JsonObject) context.serialize(item, item.getClass());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; jsonElement.addProperty(typeProperty, item.getClass().getSimpleName());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; array.add(jsonElement);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return array;&nbsp; &nbsp; }&nbsp; &nbsp; @Override&nbsp; &nbsp; public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {&nbsp; &nbsp; &nbsp; &nbsp; final Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);&nbsp; &nbsp; &nbsp; &nbsp; if (json instanceof JsonArray) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final JsonArray array = (JsonArray) json;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final int size = array.size();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (size == 0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Collections.emptyList();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; final List<?> suites = new ArrayList<>(size);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i < size; i++) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; JsonObject jsonElement = (JsonObject) array.get(i);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String simpleName = jsonElement.get(typeProperty).getAsString();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; suites.add(context.deserialize(jsonElement, getClass(simpleName, elementType)));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return suites;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return Collections.emptyList();&nbsp; &nbsp; }&nbsp; &nbsp; private Type getClass(String simpleName, Type defaultType) {&nbsp; &nbsp; &nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // you can use mapping or something else...&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Class.forName("com.model." + simpleName);&nbsp; &nbsp; &nbsp; &nbsp; } catch (ClassNotFoundException e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return defaultType;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}最大的问题是如何将类映射到值。我们可以使用类的简单名称或提供并使用它。现在,我们可以如上所述使用它。现在应用打印示例:JSONMap<String, Class>{&nbsp; "beans": [&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "baseField": "base",&nbsp; &nbsp; &nbsp; "@type": "BaseBean"&nbsp; &nbsp; },&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "childField": "child",&nbsp; &nbsp; &nbsp; "baseField": "base",&nbsp; &nbsp; &nbsp; "@type": "ChildBean"&nbsp; &nbsp; },&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; "bean2Int": 356,&nbsp; &nbsp; &nbsp; "baseField": "base",&nbsp; &nbsp; &nbsp; "@type": "ChildBean2"&nbsp; &nbsp; }&nbsp; ]}BaseBean{baseField='base'}ChildBean{baseField='base', childField='child'}ChildBean2{baseField='base', bean2Int=356}

慕码人2483693

Gson的构建考虑了“我将用于序列化”和“我将用于反序列化”。无法从原始 JSON 确定后代的确切运行时类型是什么。BaseBean您可以按照此处所述使用 - 不幸的是,它没有与基本Gson模块一起发布,也没有像这里描述的那样在Maven Central中发布。这将使用JSON发布足够的信息,允许Gson对其进行反序列化。RuntimeTypeAdapterFactory

杨魅力

更多的是附录:我只是认为至少序列化可以很好地处理数组,所以一个简单的解决方法是重新设计持有者:static class BaseBeanHolder {&nbsp; &nbsp; BaseBean[] beans;&nbsp; &nbsp; public BaseBeanHolder(BaseBean... beans) { this.beans = beans; }}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java