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

情况说明

需要解析的json

{
    "status": 200,
    "msg": "OK",
    "data": [
        {
            "id": 1,
            "username": "eric",
            "password": "123456",
            "age": 29,
            "sex": 0,
            "permission": 0,
            "isDel": 0
        }
    ]
}

类的定义

@Data
public class ResponseData implements Serializable {
    // 响应业务状态
    private Integer status;
    // 响应消息
    private String msg;
    // 响应中的数据
    private Object data;
}

解析后

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}])

进行处理后,变为

{
    "status": 200,
    "msg": "OK",
    "data": [
        {
            "id": 1.0,
            "username": "eric",
            "password": "123456",
            "age": 29.0,
            "sex": 0.0,
            "permission": 0.0,
            "isDel": 0.0
        }
    ]
}

问题分析

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);
  }
}

查看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();
        }
    }
}

第二步:注册gson

将自己重写的TypeAdapter注册一下

private static Gson buildGson() {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapterFactory(DataTypeAdaptor.FACTORY);
    return gsonBuilder.create();

}

第三步:测试

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());
    }
}

其他问题

解析过程中,还会出现转换问题,如:
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);
}


版权声明:本文为yyuggjggg原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。