使用Gson注意事项

这几天Gson可把我害惨了。不过终于搞定了。

之前对于Gson是白痴,在学习的过程中遇到了一些问题,写出来给大家分享一下:

1:程序执行到 gson.fromJson(jsonstring,class);的时候闪退

原因:json字符串里面的key与javabean里的字段数据类型不一样。Gson映射的时候数据类型比较少,一定注意。

2:部分字段为null

原因:key与javabean里的字段名字不一样。Gson不在意是哪个实体类,但很在意名字是否一致。

3:用不着的字段可以省略

4:Gson区分大小写

5:当实体为一个是用youclass name= gson.fromJson(jsonstring,new TypeToken<yourclass>(){}.getType());

      当为实体的集合是用<Listyouclass> name= gson.fromJson(jsonstring,new TypeToken<List<yourclass>>(){}.getType());

<think>我们使用Gson来处理泛型集合`List<?extendsAction>`的序列化和反序列化。由于Java的泛型擦除,我们需要在反序列化时提供具体的类型信息。以下是解决方案:###1.序列化(对象→JSON)序列化时,Gson可以直接处理`List<?extendsAction>`,因为运行时类型是具体的子类集合。```javaimportcom.google.gson.Gson;importjava.util.ArrayList;importjava.util.List;publicclassSerializationDemo{publicstaticvoidmain(String[]args){//创建具体子类对象的列表List<ActionImpl>actions=newArrayList<>();actions.add(newActionImpl("task1"));actions.add(newActionImpl("task2"));Gsongson=newGson();Stringjson=gson.toJson(actions);System.out.println(json);//输出示例:[{"name":"task1"},{"name":"task2"}]}}```###2.反序列化(JSON→对象)反序列化时,我们需要指定具体的列表类型。由于`List<?extendsAction>`本身无法直接实例化,我们需要将其反序列化为一个具体子类的列表(如`List<ActionImpl>`),然后赋值给一个声明为`List<?extendsAction>`的变量。**步骤:**1.使用`TypeToken`捕获泛型类型信息。2.使用`fromJson`方法并传递正确的`Type`。```javaimportcom.google.gson.Gson;importcom.google.gson.reflect.TypeToken;importjava.lang.reflect.Type;importjava.util.List;publicclassDeserializationDemo{publicstaticvoidmain(String[]args){Stringjson="[{\"name\":\"task1\"},{\"name\":\"task2\"}]";Gsongson=newGson();//使用TypeToken指定具体类型:List<ActionImpl>Typetype=newTypeToken<List<ActionImpl>>(){}.getType();List<?extendsAction>actions=gson.fromJson(json,type);//使用列表中的对象for(Actionaction:actions){System.out.println(action.execute());}}}```###3.处理多态类型如果列表中包含多种`Action`子类,我们需要在序列化和反序列化时处理多态。Gson默认不保存类型信息,因此我们需要自定义序列化策略。####方案:注册类型适配器(TypeAdapter)或使用RuntimeTypeAdapterFactory**步骤:**1.添加Gson扩展库(如`gson-extras`)以使用`RuntimeTypeAdapterFactory`。2.配置Gson实例,注册多态类型处理。**Maven依赖:**```xml<dependency><groupId>org.danilopianini</groupId><artifactId>gson-extras</artifactId><version>0.2.0</version></dependency>```**代码示例:**```javaimportcom.google.gson.Gson;importcom.google.gson.GsonBuilder;importorg.danilopianini.gson.extras.RuntimeTypeAdapterFactory;publicclassPolymorphicDemo{publicstaticvoidmain(String[]args){//创建支持多态的RuntimeTypeAdapterFactoryRuntimeTypeAdapterFactory<Action>typeFactory=RuntimeTypeAdapterFactory.of(Action.class,"type")//指定基类和类型字段名.registerSubtype(ActionImpl.class,"action_impl")//注册子类.registerSubtype(OtherAction.class,"other_action");Gsongson=newGsonBuilder().registerTypeAdapterFactory(typeFactory).create();//序列化List<Action>actions=newArrayList<>();actions.add(newActionImpl("task1"));actions.add(newOtherAction("special_task"));Stringjson=gson.toJson(actions);System.out.println(json);//输出:[{"type":"action_impl","name":"task1"},{"type":"other_action","id":"special_task"}]//反序列化Typetype=newTypeToken<List<Action>>(){}.getType();List<Action>deserialized=gson.fromJson(json,type);deserialized.forEach(System.out::println);}}```###4.核心注意事项-**类型安全**:反序列化后,列表中的对象是具体的子类实例,可以直接使用。-**无参构造器**:确保子类有无参构造器(或使用`@Expose`等注解控制字段),否则Gson可能无法实例化对象。-**字段可见性**:子类字段应为`public`或提供`public`的getter/setter,否则Gson可能无法访问。###5.性能优化-**复用Gson实例**:避免重复创建(线程安全)。-**缓存TypeToken**:对于频繁使用的类型,缓存`TypeToken`对象。>**警告**:如果JSON数组中包含多种子类对象,必须使用多态处理方案(如`RuntimeTypeAdapterFactory`),否则反序列化会失败。§§相关问题§§1.Gson中如何处理循环引用?2.如何自定义Gson的日期格式?3.Gson与Jackson在多态处理上的区别是什么?4.如何在SpringBoot中配置Gson作为默认JSON处理器?</think>### 使用 Gson 处理泛型集合 `List<? extends Action>` 的 JSON 转换 #### 一、解决方案概述 Gson 通过 `TypeToken` 解决泛型类型擦除问题,结合多态类型适配器实现接口泛型集合的转换。以下是完整实现: #### 二、序列化(对象 → JSON) ```java import com.google.gson.Gson; import java.util.List; public class SerializationDemo { public static void main(String[] args) { List<? extends Action> actions = List.of( new ActionImpl("task1"), new OtherAction(123) ); Gson gson = new Gson(); String json = gson.toJson(actions); System.out.println(json); // 输出: [{"type":"action_impl","name":"task1"},{"type":"other_action","code":123}] } } ``` #### 三、反序列化(JSON → 对象) ```java import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.List; public class DeserializationDemo { public static void main(String[] args) { String json = "[{\"type\":\"action_impl\",\"name\":\"task1\"}]"; // 1. 创建支持多态的 Gson 实例 Gson gson = createPolymorphicGson(); // 2. 通过 TypeToken 捕获泛型类型 Type type = new TypeToken<List<? extends Action>>(){}.getType(); // 3. 执行反序列化 List<? extends Action> actions = gson.fromJson(json, type); actions.forEach(a -> System.out.println(a.execute())); } } ``` #### 四、核心配置 ##### 1. 定义多态类型适配器(关键) ```java import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import java.io.IOException; public class PolymorphicAdapter extends TypeAdapter<Action> { private static final String TYPE_FIELD = "type"; @Override public void write(JsonWriter out, Action value) throws IOException { // 序列化时自动添加类型标识 out.beginObject(); out.name(TYPE_FIELD).value(value.getClass().getSimpleName()); // 使用 Gson 默认序列化其他字段 Gson gson = new Gson(); gson.toJson(value, value.getClass(), out); out.endObject(); } @Override public Action read(JsonReader in) throws IOException { // 动态识别类型并反序列化 in.beginObject(); String typeName = null; Action result = null; while (in.hasNext()) { String field = in.nextName(); if (TYPE_FIELD.equals(field)) { typeName = in.nextString(); } else { in.skipValue(); // 跳过非类型字段 } } in.endObject(); // 根据类型标识创建实例 if ("ActionImpl".equals(typeName)) { result = new Gson().fromJson(in, ActionImpl.class); } else if ("OtherAction".equals(typeName)) { result = new Gson().fromJson(in, OtherAction.class); } return result; } } ``` ##### 2. 创建支持多态的 Gson 实例 ```java private static Gson createPolymorphicGson() { return new GsonBuilder() .registerTypeAdapter(Action.class, new PolymorphicAdapter()) .create(); } ``` ##### 3. 接口和实现类定义 ```java public interface Action { String execute(); } public class ActionImpl implements Action { private String name; // 必须有无参构造器 public ActionImpl() {} public ActionImpl(String name) { this.name = name; } @Override public String execute() { return "Executing: " + name; } } public class OtherAction implements Action { private int code; public OtherAction() {} public OtherAction(int code) { this.code = code; } @Override public String execute() { return "Code: " + code; } } ``` #### 五、关键问题解决 | 问题 | Gson 解决方案 | |---------------------|-----------------------------------| | **泛型类型擦除** | 使用 `TypeToken` 捕获泛型类型信息[^2] | | **多态类型识别** | 自定义 `TypeAdapter` 添加类型标识 | | **无参构造缺失** | 所有子类必须提供默认构造器 | | **字段过滤** | 使用 `@Expose(serialize=false)` 或 `transient` | #### 六、最佳实践建议 1. **类型标识字段**:使用固定字段(如 `type`)存储类名,确保反序列化可识别 2. **注册子类型**:在适配器中维护类型映射表,避免硬编码类名 3. **安全校验**:反序列化后添加类型检查 ```java actions.stream() .filter(a -> a instanceof ActionImpl) .map(ActionImpl.class::cast) .forEach(System.out::println); ``` 4. **性能优化**:复用 Gson 实例(线程安全) > **注意**:当 JSON 包含多种子类型时,必须通过类型字段明确区分,否则会反序列化失败。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值