Gson: GSON is always converting a long number to scientific notation format.

Created on 28 Nov 2016  路  10Comments  路  Source: google/gson

The issue is described at StackOverflow.

GSON always converts long numbers ending with zero to a scientific format (e.g. 1.4803202E9) and there is no direct way of preserving the number format with zeros.

Most helpful comment

This is actually more severe than described until now. I have cases where I have a long number, e.g. 5992738213938843957 which GSON returns as 5.9927382139388436E18, which a Double.toLong() returns as 5992738213938843648! This is because decimal places in Double are not exact like BigDecimal. So I do not have a way to get the long value out with GSON at all currently.

See also https://stackoverflow.com/questions/17695242 and https://stackoverflow.com/questions/15507997

So it seems the only option is to switch to Jackson here.

All 10 comments

I think, it's only happen when GSON don't know that it's a long type. Look at simple example of comparison with Jackson

void testDeserializer() throws IOException {
        String jsonString = "{\"id\":10389321}";

        Gson gson = new Gson();
        IdWrapper gsonIdWrapper = gson.fromJson(jsonString, IdWrapper.class);
        String gsonId = gsonIdWrapper.getId(); // id is Double: 1.0389321E7

        ObjectMapper mapper = new ObjectMapper();
        IdWrapper jacksonIdWrapper = mapper.readValue(jsonString, IdWrapper.class);
        String jacksonId = jacksonIdWrapper.getId(); // id is Integer: 10389321
    }

    public static class IdWrapper {
        private Object id; // if id is int, gson works fine
        public String getId() {
            return id.toString();
        }
    } 

And take a look at another thread of StackOverflow

especially timestamp~ waiting for solutions.

The origin of the problem is because in ObjectTypeAdapter class (at line 78), when it detects the value is a Number returns a Double by default (even if it is an int) and by "fixing" this section of code, other tests will fail because is meant to work that way (returning always a double).

I would recommend you either to give the variable the appropriate format after retrieving the value through GSON if you don't want the scientific notation or changing the type of variable that will store the value. If you want to treat the value with a different format maybe DecimalFormat class might help you with that.

An important point is to notice as @Parmakli says, this only happens when GSON doesn't know exactly what type of number you want so if it is possible you could try to be more precise when treating the type of value to avoid this problem.

I believe that the problem is due to a lack of an ability to explicitly specify a desired format to be used for long numbers.

I think the root problem in Gson is that when ObjectTypeAdapter encounters a NUMBER it calls JsonReader.nextDouble(), so now all you have is a Double, and its normal string representation of whole numbers always ends in .0 or has an exponent suffix.

If this is correct, Gson would need to replace uses of JsonReader.nextDouble() with a method like JsonReader.nextBigDecimal() and many other related changes. The BigDecimal.toPlainString() method should be used for the String representation.

(from here)

At the very least, when supplying your own TypeAdapter, so that when it peeks a NUMBER and calls JsonReader.nextString, it should actually return the raw string representation of the number in the JSON. In current code, the String returned always has ".0" attached to integer values, so you can't accurately create a BigDecimal from the string!

waiting for solutions.

You could either convert Double to Long by hand, or use Jackson instead, which would parse the number as expected.
There is a summary & example code in this link: https://stackoverflow.com/a/48230990/1568658

This is actually more severe than described until now. I have cases where I have a long number, e.g. 5992738213938843957 which GSON returns as 5.9927382139388436E18, which a Double.toLong() returns as 5992738213938843648! This is because decimal places in Double are not exact like BigDecimal. So I do not have a way to get the long value out with GSON at all currently.

See also https://stackoverflow.com/questions/17695242 and https://stackoverflow.com/questions/15507997

So it seems the only option is to switch to Jackson here.

Any idea how to fix it?

Was this page helpful?
0 / 5 - 0 ratings