如何防止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
要使用它,您必须为registerTypeAdapter
和gson.fromJson
函数提供正确的TypeToken
:
String json="[{\"id\":1,\"quantity\":2,\"name\":\"apple\"}, {\"id\":3,\"quantity\":4,\"name\":\"orange\"}]"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(new TypeToken
结果:
{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);
您必须将此类型适配器注册到您需要的那些类型,例如Map
和List
:
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映射到IdQuantityName
而IdQuantityDeserializer
是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();