如何防止gson将整数转换为双精度数

我的json中有整数,我不希望gson将它们转换成双打。 以下不起作用:

@Test public void keepsIntsAsIs(){ String json="[{\"id\":1,\"quantity\":2,\"name\":\"apple\"},{\"id\":3,\"quantity\":4,\"name\":\"orange\"}]"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Double.class, new DoubleSerializerAsInt()); Gson gson = gsonBuilder.create(); List<Map> l = gson.fromJson(json, List.class); for(Map item : l){ System.out.println(item); } } private static class DoubleSerializerAsInt implements JsonSerializer{ @Override public JsonElement serialize(Double aDouble, Type type, JsonSerializationContext jsonSerializationContext) { int value = (int)Math.round(aDouble); return new JsonPrimitive(value); } } 

输出不是我想要的:

 {id=1.0, quantity=2.0, name=apple} {id=3.0, quantity=4.0, name=orange} 

有没有办法在我的地图中使用整数而不是双打?

 {id=1, quantity=2, name=apple} {id=3, quantity=4, name=orange} 

编辑:并非所有字段都是整数。 我相应地修改了我的例子。 我在网上看了很多例子,包括这个网站上的一些答案,但在这个特殊情况下它不起作用。

1)您必须在您的问题中创建自定义JsonDeserializer而不是JsonSerializer

2)我不认为这种行为来自Double deserializer。 它更像是json对象/地图问题

这是源代码 :

 case NUMBER: return in.nextDouble(); 

因此,您可以尝试使用Map自定义反序列化器(或者如果需要,可以使用更通用的地图):

 public static class MapDeserializerDoubleAsIntFix implements JsonDeserializer>{ @Override @SuppressWarnings("unchecked") public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return (Map) read(json); } public Object read(JsonElement in) { if(in.isJsonArray()){ List list = new ArrayList(); JsonArray arr = in.getAsJsonArray(); for (JsonElement anArr : arr) { list.add(read(anArr)); } return list; }else if(in.isJsonObject()){ Map map = new LinkedTreeMap(); JsonObject obj = in.getAsJsonObject(); Set> entitySet = obj.entrySet(); for(Map.Entry entry: entitySet){ map.put(entry.getKey(), read(entry.getValue())); } return map; }else if( in.isJsonPrimitive()){ JsonPrimitive prim = in.getAsJsonPrimitive(); if(prim.isBoolean()){ return prim.getAsBoolean(); }else if(prim.isString()){ return prim.getAsString(); }else if(prim.isNumber()){ Number num = prim.getAsNumber(); // here you can handle double int/long values // and return any type you want // this solution will transform 3.0 float to long values if(Math.ceil(num.doubleValue()) == num.longValue()) return num.longValue(); else{ return num.doubleValue(); } } } return null; } } 

要使用它,您必须为registerTypeAdaptergson.fromJson函数提供正确的TypeToken

 String json="[{\"id\":1,\"quantity\":2,\"name\":\"apple\"}, {\"id\":3,\"quantity\":4,\"name\":\"orange\"}]"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(new TypeToken>(){}.getType(), new MapDeserializerDoubleAsIntFix()); Gson gson = gsonBuilder.create(); List> l = gson.fromJson(json, new TypeToken>>(){}.getType() ); for(Map item : l) System.out.println(item); String serialized = gson.toJson(l); System.out.println(serialized); 

结果:

 {id=1, quantity=2, name=apple} {id=3, quantity=4, name=orange} Serialized back to: [{"id":1,"quantity":2,"name":"apple"},{"id":3,"quantity":4,"name":"orange"}] 

PS:这是你可以尝试的另一种选择。 我个人觉得为你的json创建自定义对象而不是List>更酷更容易阅读

流媒体版@ varren的回答:

 class CustomizedObjectTypeAdapter extends TypeAdapter { private final TypeAdapter delegate = new Gson().getAdapter(Object.class); @Override public void write(JsonWriter out, Object value) throws IOException { delegate.write(out, value); } @Override public Object read(JsonReader in) throws IOException { JsonToken token = in.peek(); switch (token) { case BEGIN_ARRAY: List list = new ArrayList(); in.beginArray(); while (in.hasNext()) { list.add(read(in)); } in.endArray(); return list; case BEGIN_OBJECT: Map map = new LinkedTreeMap(); 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(); String n = in.nextString(); if (n.indexOf('.') != -1) { return Double.parseDouble(n); } return Long.parseLong(n); case BOOLEAN: return in.nextBoolean(); case NULL: in.nextNull(); return null; default: throw new IllegalStateException(); } } } 

它是ObjectTypeAdapter.java的修改版本。 这些原始行:

 case NUMBER: return in.nextDouble(); 

被这取代:

 case NUMBER: String n = in.nextString(); if (n.indexOf('.') != -1) { return Double.parseDouble(n); } return Long.parseLong(n); 

在这段代码中,数字被读作字符串,数字的类型是根据点的存在来选择的:只有在字符串表示中有一个点时,数字才是双倍的,否则它是长的。 此类解决方案保留源JSON的原始值。

如果你可以为Object类型注册它,但是这个修改过的适配器可以用作通用的,但Gson会阻止它:

 // built-in type adapters that cannot be overridden factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); factories.add(ObjectTypeAdapter.FACTORY); 

您必须将此类型适配器注册到您需要的那些类型,例如MapList

 CustomizedObjectTypeAdapter adapter = new CustomizedObjectTypeAdapter(); Gson gson = new GsonBuilder() .registerTypeAdapter(Map.class, adapter) .registerTypeAdapter(List.class, adapter) .create(); 

现在Gson可以按原样反序列化数字。

你必须使用public T fromJson(JsonElement json,Type typeOfT)

 public void keepsIntsAsIs(){ String json="[{\"id\":1,\"quantity\":2},{\"id\":3,\"quantity\":4}]"; GsonBuilder gsonBuilder = new GsonBuilder(); Gson gson = gsonBuilder.create(); Type objectListType = new TypeToken>>(){}.getType(); List> l = gson.fromJson(json, objectListType); for(Map item : l){ System.out.println(item); } } 

输出:

 {id=1, quantity=2} {id=3, quantity=4} 

[编辑]

如果不是所有字段都是整数,那么解决此问题的一种方法是将json映射到对象并为该对象定义反序列化器。

以下是示例。

我将json映射到IdQuantityNameIdQuantityDeserializer是json反序列化器。

 package com.foo; import java.lang.reflect.Type; import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.reflect.TypeToken; public class TestGSON { public void keepsIntsAsIs(){ String json="[{\"id\":1,\"quantity\":2,\"name\":\"apple\"},{\"id\":3,\"quantity\":4,\"name\":\"orange\"}]"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeHierarchyAdapter(IdQuantityName.class, new IdQuantityDeserializer()); gsonBuilder.registerTypeAdapter(IdQuantityName.class, new IdQuantityDeserializer()); Gson gson = gsonBuilder.create(); Type objectListType = new TypeToken>(){}.getType(); List l = gson.fromJson(json,objectListType); for (IdQuantityName idQuantityName : l) { System.out.println(idQuantityName); } } class IdQuantityName{ private int id; private Object quantity; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public Object getQuantity() { return quantity; } public void setQuantity(Object quantity) { this.quantity = quantity; } public Object getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "IdQuantityName [id=" + id + ", quantity=" + quantity + ", name=" + name + "]"; } } private class IdQuantityDeserializer implements JsonDeserializer{ @Override public IdQuantityName deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jo = json.getAsJsonObject(); IdQuantityName idq = new IdQuantityName(); idq.setId(jo.get("id").getAsInt()); idq.setName(jo.get("name").getAsString()); JsonElement jsonElement = jo.get("quantity"); if(jsonElement instanceof JsonPrimitive){ if(((JsonPrimitive) jsonElement).isNumber()){ idq.setQuantity(jsonElement.getAsInt()); }; } return idq; } } public static void main(String[] args) { new TestGSON().keepsIntsAsIs(); } } 

这对我来说很好:

 private static class DoubleSerializer implements JsonSerializer { @Override public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) { return src == src.longValue() ? new JsonPrimitive(src.longValue()) : new JsonPrimitive(src); } } Gson gson = new GsonBuilder().registerTypeAdapter(Double.class, new DoubleSerializer()).setPrettyPrinting().create();