• Gson转换错误导致Int变为Double类型


    情况说明

    需要解析的json

    {
        "status": 200,
        "msg": "OK",
        "data": [
            {
                "id": 1,
                "username": "eric",
                "password": "123456",
                "age": 29,
                "sex": 0,
                "permission": 0,
                "isDel": 0
            }
        ]
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    类的定义

    @Data
    public class ResponseData implements Serializable {
        // 响应业务状态
        private Integer status;
        // 响应消息
        private String msg;
        // 响应中的数据
        private Object data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解析后

    ResponseData(status=200, msg=OK, data=[{id=1.0, username=eric, password=123456, age=29.0, sex=0.0, permission=0.0, isDel=0.0}])
    
    
    • 1
    • 2

    进行处理后,变为

    {
        "status": 200,
        "msg": "OK",
        "data": [
            {
                "id": 1.0,
                "username": "eric",
                "password": "123456",
                "age": 29.0,
                "sex": 0.0,
                "permission": 0.0,
                "isDel": 0.0
            }
        ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    问题分析

    Object最后默认的TypeAdapter使用的是com.google.gson.internal.bind包下的ObjectTypeAdapter,处理如下

    /**
     * Adapts types whose static type is only 'Object'. Uses getClass() on
     * serialization and a primitive/Map/List on deserialization.
     */
    public final class ObjectTypeAdapter extends TypeAdapter<Object> {
      public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        @SuppressWarnings("unchecked")
        @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
          if (type.getRawType() == Object.class) {
            return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
          }
          return null;
        }
      };
    
      private final Gson gson;
    
      ObjectTypeAdapter(Gson gson) {
        this.gson = gson;
      }
    
      @Override public Object read(JsonReader in) throws IOException {
        JsonToken token = in.peek();
        switch (token) {
        case BEGIN_ARRAY:
          List<Object> list = new ArrayList<Object>();
          in.beginArray();
          while (in.hasNext()) {
            list.add(read(in));
          }
          in.endArray();
          return list;
    
        case BEGIN_OBJECT:
          Map<String, Object> map = new LinkedTreeMap<String, Object>();
          in.beginObject();
          while (in.hasNext()) {
            map.put(in.nextName(), read(in));
          }
          in.endObject();
          return map;
    
        case STRING:
          return in.nextString();
    
        case NUMBER:
          return in.nextDouble();
    
        case BOOLEAN:
          return in.nextBoolean();
    
        case NULL:
          in.nextNull();
          return null;
    
        default:
          throw new IllegalStateException();
        }
      }
    
      @SuppressWarnings("unchecked")
      @Override public void write(JsonWriter out, Object value) throws IOException {
        if (value == null) {
          out.nullValue();
          return;
        }
    
        TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
        if (typeAdapter instanceof ObjectTypeAdapter) {
          out.beginObject();
          out.endObject();
          return;
        }
    
        typeAdapter.write(out, value);
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    查看read方法发现所有的Number类型都被转成了double类型,想要修改掉这个问题,需要自己实现一个TypeAdapter,处理Number类型的问题,自己重写TypeAdapter

    解决

    上面我们分析后,是需要去重写TypeAdapter方法,在重写的这个方法里面,我们需要将其写出为对应的方法

    第一步:重写TypeAdapter方法

    public class ResponseDataTypeAdaptor extends TypeAdapter<ResponseData> {
        public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
            @SuppressWarnings("unchecked")
            @Override
            public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
                if (type.getRawType() == ResponseData.class) {
                    return (TypeAdapter<T>) new ResponseDataTypeAdaptor(gson);
                }
                return null;
            }
        };
        private final Gson gson;
    
        ResponseDataTypeAdaptor(Gson gson) {
            this.gson = gson;
        }
    
        @Override
        public void write(JsonWriter out, ResponseData value) throws IOException {
            if (value == null) {
                out.nullValue();
                return;
            }
            out.beginObject();
            out.name("status");
            gson.getAdapter(Integer.class).write(out, value.getStatus());
            out.name("msg");
            gson.getAdapter(String.class).write(out, value.getMsg());
            out.name("data");
            gson.getAdapter(Object.class).write(out, value.getData());
            out.endObject();
        }
    
        @Override
        public ResponseData read(JsonReader in) throws IOException {
            ResponseData data = new ResponseData();
            Map<String, Object> dataMap = (Map<String, Object>) readInternal(in);
            data.setStatus((Integer) dataMap.get("status"));
            data.setMsg((String) dataMap.get("msg"));
            data.setData(dataMap.get("data"));
            return data;
        }
    
        private Object readInternal(JsonReader in) throws IOException {
            JsonToken token = in.peek();
            switch (token) {
                case BEGIN_ARRAY:
                    List<Object> list = new ArrayList<Object>();
                    in.beginArray();
                    while (in.hasNext()) {
                        list.add(readInternal(in));
                    }
                    in.endArray();
                    return list;
                case BEGIN_OBJECT:
                    Map<String, Object> map = new LinkedTreeMap<String, Object>();
                    in.beginObject();
                    while (in.hasNext()) {
                        map.put(in.nextName(), readInternal(in));
                    }
                    in.endObject();
                    return map;
                case STRING:
                    return in.nextString();
                case NUMBER:
                    String numberStr = in.nextString();
                    if (numberStr.contains(".") || numberStr.contains("e") || numberStr.contains("E")) {
                        return Double.parseDouble(numberStr);
                    }
                    if (Long.parseLong(numberStr) <= Integer.MAX_VALUE) {
                        return Integer.parseInt(numberStr);
                    }
                    return Long.parseLong(numberStr);
                case BOOLEAN:
                    return in.nextBoolean();
                case NULL:
                    in.nextNull();
                    return null;
                default:
                    throw new IllegalStateException();
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    第二步:注册gson

    将自己重写的TypeAdapter注册一下

    private static Gson buildGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapterFactory(DataTypeAdaptor.FACTORY);
        return gsonBuilder.create();
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第三步:测试

    public class GsonTest {
        public static void main(String[] args) {
            String dataJson = "{\"status\":200,\"msg\":\"OK\",\"data\":[{\"id\":1,\"username\":\"eric\",\"password\":\"123456\",\"age\":29,\"sex\":0,\"permission\":0,\"isDel\":0}]}";
            Gson gson = buildGson();
            ResponseData data = gson.fromJson(dataJson, ResponseData.class);
            System.out.println(data.toString());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    其他问题

    解析过程中,还会出现转换问题,如:
    com.google.gson.internal.LinkedTreeMap cannot be cast to XXX

    问题1:LinkedTreeMap cannot be cast to XXX

    这种情况出现的原因是:
    进行到对list进行操作的那步,报错了(java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to xx)。原来list中的数据是LinkedTreeMap 格式的,并没有转换成对应的实体类。

    那么现在就需要自己去改写一下之前所重写的TypeAdapter方法

    问题1:解决

    去遍历这个LinkedTreeMap 这个Map

    LinkedTreeMap,可通过key来获取value的map

    如下去一步一步遍历,将遍历的结果设置到对应的实体类中

    LinkedTreeMap tm = (LinkedTreeMap)fromJson2;
    Iterator it = tm.keySet().iterator();  
    while (it.hasNext()) {  
        String key = (String) it.next();
        String value = (String)tm.get(key);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    
    
    • 1
  • 相关阅读:
    不良事件上报系统
    低代码平台自动化办公--异行星低代码平台为例(一)
    Error: impossible constraint in ‘asm‘
    Windows连接Linux上安装的Redis
    Java ME SDK 3.0
    微服务实战 03 Sentinel 限流、熔断降级解析和配置
    实验18:模拟温度传感器实验
    自媒体写手提问常用的ChatGPT通用提示词模板
    CSS三大定位方式(浮动、定位、弹性盒)详细解析
    为什么私有化的知识库平台对企业来说更靠谱呢?
  • 原文地址:https://blog.csdn.net/yyuggjggg/article/details/126777376