I’ve got integers in my json, and I do not want gson to convert them to doubles. The following does not work:
@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<String, Object>> l = gson.fromJson(json, List.class); for(Map<String, Object> item : l){ System.out.println(item); } } private static class DoubleSerializerAsInt implements JsonSerializer<Double>{ @Override public JsonElement serialize(Double aDouble, Type type, JsonSerializationContext jsonSerializationContext) { int value = (int)Math.round(aDouble); return new JsonPrimitive(value); } }
The output is not what I want:
{id=1.0, quantity=2.0, name=apple} {id=3.0, quantity=4.0, name=orange}
Is there a way to have Integers instead of Doubles in my Map?
{id=1, quantity=2, name=apple} {id=3, quantity=4, name=orange}
Edit: not all my fields are integer. I’ve modified my example accordingly. I’ve read quite a few examples online, including some answers on this site, but it does not work in this particular case.
Advertisement
Answer
1) You have to create custom JsonDeserializer
and not JsonSerializer
like in your question.
2) I don’t think this behavior comes from Double
deserializer. it is more like json object/map problem
Here is from source code:
case NUMBER: return in.nextDouble();
So you can try approach with custom deserializer for Map<String, Object>
(or some more generic map if you want) :
public static class MapDeserializerDoubleAsIntFix implements JsonDeserializer<Map<String, Object>>{ @Override @SuppressWarnings("unchecked") public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return (Map<String, Object>) read(json); } public Object read(JsonElement in) { if(in.isJsonArray()){ List<Object> list = new ArrayList<Object>(); JsonArray arr = in.getAsJsonArray(); for (JsonElement anArr : arr) { list.add(read(anArr)); } return list; }else if(in.isJsonObject()){ Map<String, Object> map = new LinkedTreeMap<String, Object>(); JsonObject obj = in.getAsJsonObject(); Set<Map.Entry<String, JsonElement>> entitySet = obj.entrySet(); for(Map.Entry<String, JsonElement> 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; } }
To use it you will have to give proper TypeToken
to registerTypeAdapter
and gson.fromJson
function:
String json="[{"id":1,"quantity":2,"name":"apple"}, {"id":3,"quantity":4,"name":"orange"}]"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(new TypeToken<Map <String, Object>>(){}.getType(), new MapDeserializerDoubleAsIntFix()); Gson gson = gsonBuilder.create(); List<Map<String, Object>> l = gson.fromJson(json, new TypeToken<List<Map<String, Object>>>(){}.getType() ); for(Map<String, Object> item : l) System.out.println(item); String serialized = gson.toJson(l); System.out.println(serialized);
Result:
{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: It is just one more option you can try. Personally i feel like creating custom object for your json instead of List<Map<String, Integer>>
is much cooler and easier to read way