Vojtěch Vojtěch - 22 days ago 7
Java Question

Make Gson to parse integers and not as floats

I am trying to make

Gson
to parse values without floating point as Longs and not as Doubles as it defaultly does:

Gson gson = new GsonBuilder()
.registerTypeAdapter(Object.class, (JsonDeserializer<Object>) (json, type, context) -> {
// we don't want integer values to be parsed as floats
String value = json.getAsJsonPrimitive().getAsString();
if (value.matches("\\d+")) {
return Long.valueOf(value);
} else {
return context.deserialize(json, type);
}
}).create();
...
gson.fromJson(payload);


I just want to test, weather the value contains the
.
and if not, parse it as
Long
. Otherwise use the default parsing method.

However when I put there breakpoints, the method is never called. I suppose I do the registration of the adapter in a wrong way.

EDIT1: The Json might contain lists with strings, integers, doubles and maps. I need a general solution.

EDIT2: I also tried varieties of
.registerTypeAdapter(Number.class
or
.registerTypeAdapter(Double.class
with no effect. Still not called.

Answer

I couldn't find a better way than to completely override the Gson function. It is quite a hack, but no other solution seem to fix the issue:

public class Gson {

    private static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        public <T> TypeAdapter<T> create(com.google.gson.Gson gson, TypeToken<T> type) {
            return type.getRawType() == Object.class ? (TypeAdapter<T>) new LongObjectTypeAdapter(gson) : null;
        }
    };



    static {
        try {
            Field field = ObjectTypeAdapter.class.getDeclaredField("FACTORY");

            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

            field.setAccessible(true);
            field.set(null, FACTORY);

        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }



    private com.google.gson.Gson gson = new com.google.gson.Gson();



    public <T> T fromJson(String payload, Class<T> c) {
        return gson.fromJson(payload, c);
    }



    public String toJson(Object object) {
        return gson.toJson(object);
    }



    public static class LongObjectTypeAdapter extends TypeAdapter<Object> {

        private com.google.gson.Gson gson;

        public LongObjectTypeAdapter(com.google.gson.Gson gson) {
            this.gson = gson;
        }

        public Object read(JsonReader in) throws IOException {
            JsonToken token = in.peek();

            switch (token) {
                case BEGIN_ARRAY:
                    ArrayList list = new ArrayList();
                    in.beginArray();

                    while(in.hasNext()) {
                        list.add(this.read(in));
                    }

                    in.endArray();
                    return list;
                case BEGIN_OBJECT:
                    LinkedTreeMap map = new LinkedTreeMap();
                    in.beginObject();

                    while(in.hasNext()) {
                        map.put(in.nextName(), this.read(in));
                    }

                    in.endObject();
                    return map;
                case STRING:
                    return in.nextString();
                case NUMBER:
                    String value = in.nextString();
                    if (value.contains(".")) {
                        return Double.valueOf(value);
                    } else {
                        return Long.valueOf(value);
                    }

                case BOOLEAN:
                    return Boolean.valueOf(in.nextBoolean());
                case NULL:
                    in.nextNull();
                    return null;
                default:
                    throw new IllegalStateException();
            }
        }

        public void write(JsonWriter out, Object value) throws IOException {
            if (value == null) {
                out.nullValue();
            } else {
                TypeAdapter typeAdapter = gson.getAdapter(value.getClass());
                if (typeAdapter instanceof LongObjectTypeAdapter) {
                    out.beginObject();
                    out.endObject();
                } else {
                    typeAdapter.write(out, value);
                }
            }
        }
    }
}