为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

红糖糍粑 2022-08-17 10:40:09
请考虑以下示例: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; }}@Testpublic 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实例来自哪里以及如何在我们的环境中配置它)
查看完整描述

3 回答

?
慕斯王

TA贡献1864条经验 获得超2个赞

通常,集合实现从集合字段声明“take”类型 - 而不是从 //etc 上的给定项。我们需要编写自定义序列化器,为每个项目查找序列化程序并使用它。实现简单:ListSet


class TypeAwareListJsonSeserializer implements JsonSerializer<List<?>> {

    @Override

    public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {

        if (src == null) {

            return JsonNull.INSTANCE;

        }

        JsonArray array = new JsonArray();

        for (Object item : src) {

            JsonElement jsonElement = context.serialize(item, item.getClass());

            array.add(jsonElement);

        }

        return array;

    }

}

这就是我们如何使用它:


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 {


    public static void main(String[] args) throws Exception {

        List<BaseBean> children = Arrays.asList(new BaseBean(), new ChildBean(), new ChildBean2());

        BaseBeanHolder baseHolder = new BaseBeanHolder(children);

        Gson gson = new GsonBuilder()

                .setPrettyPrinting()

                .create();

        System.out.println(gson.toJson(baseHolder));

    }

}


class BaseBean {

    String baseField = "base";

}


class ChildBean extends BaseBean {

    String childField = "child";

}


class ChildBean2 extends BaseBean {

    int bean2Int = 356;

}


class BaseBeanHolder {


    @JsonAdapter(TypeAwareListJsonSeserializer.class)

    private List<? extends BaseBean> beans;


    // getters, setters, toString

}

以上代码打印:


{

  "beans": [

    {

      "baseField": "base"

    },

    {

      "childField": "child",

      "baseField": "base"

    },

    {

      "bean2Int": 356,

      "baseField": "base"

    }

  ]

}

编辑

在序列化过程中,我们会丢失有关反序列化过程中所需的类型的信息。我开发了简单的类型信息,这些信息将在序列化期间存储并用于反序列化。它可能如下所示:


class TypeAwareListJsonAdapter implements JsonSerializer<List<?>>, JsonDeserializer<List<?>> {


    private final String typeProperty = "@type";


    @Override

    public JsonElement serialize(List<?> src, Type typeOfSrc, JsonSerializationContext context) {

        if (src == null) {

            return JsonNull.INSTANCE;

        }

        JsonArray array = new JsonArray();

        for (Object item : src) {

            JsonObject jsonElement = (JsonObject) context.serialize(item, item.getClass());

            jsonElement.addProperty(typeProperty, item.getClass().getSimpleName());


            array.add(jsonElement);

        }

        return array;

    }


    @Override

    public List<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        final Type elementType = $Gson$Types.getCollectionElementType(typeOfT, List.class);


        if (json instanceof JsonArray) {

            final JsonArray array = (JsonArray) json;

            final int size = array.size();

            if (size == 0) {

                return Collections.emptyList();

            }


            final List<?> suites = new ArrayList<>(size);

            for (int i = 0; i < size; i++) {

                JsonObject jsonElement = (JsonObject) array.get(i);

                String simpleName = jsonElement.get(typeProperty).getAsString();

                suites.add(context.deserialize(jsonElement, getClass(simpleName, elementType)));

            }


            return suites;

        }


        return Collections.emptyList();

    }


    private Type getClass(String simpleName, Type defaultType) {

        try {

            // you can use mapping or something else...

            return Class.forName("com.model." + simpleName);

        } catch (ClassNotFoundException e) {

            return defaultType;

        }

    }

}

最大的问题是如何将类映射到值。我们可以使用类的简单名称或提供并使用它。现在,我们可以如上所述使用它。现在应用打印示例:JSONMap<String, Class>


{

  "beans": [

    {

      "baseField": "base",

      "@type": "BaseBean"

    },

    {

      "childField": "child",

      "baseField": "base",

      "@type": "ChildBean"

    },

    {

      "bean2Int": 356,

      "baseField": "base",

      "@type": "ChildBean2"

    }

  ]

}

BaseBean{baseField='base'}

ChildBean{baseField='base', childField='child'}

ChildBean2{baseField='base', bean2Int=356}


查看完整回答
反对 回复 2022-08-17
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

Gson的构建考虑了“我将用于序列化”“我将用于反序列化”。

无法从原始 JSON 确定后代的确切运行时类型是什么。BaseBean

您可以按照此处所述使用 - 不幸的是,它没有与基本Gson模块一起发布,也没有像这里描述的那样在Maven Central中发布。这将使用JSON发布足够的信息,允许Gson对其进行反序列化。RuntimeTypeAdapterFactory


查看完整回答
反对 回复 2022-08-17
?
杨魅力

TA贡献1811条经验 获得超6个赞

更多的是附录:我只是认为至少序列化可以很好地处理数组,所以一个简单的解决方法是重新设计持有者:


static class BaseBeanHolder {

    BaseBean[] beans;

    public BaseBeanHolder(BaseBean... beans) { this.beans = beans; }

}


查看完整回答
反对 回复 2022-08-17
  • 3 回答
  • 0 关注
  • 139 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信