Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GSON is always converting a long number to scientific notation format. #968

Closed
trknz opened this issue Nov 28, 2016 · 14 comments
Closed

GSON is always converting a long number to scientific notation format. #968

trknz opened this issue Nov 28, 2016 · 14 comments

Comments

@trknz
Copy link

trknz commented Nov 28, 2016

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.

@trknz trknz changed the title GSON always converting a long number to to scientific notation format. GSON is always converting a long number to scientific notation format. Nov 28, 2016
@Parmakli
Copy link

Parmakli commented Dec 6, 2016

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

@pengoC
Copy link

pengoC commented Dec 8, 2016

especially timestamp~ waiting for solutions.

@AlanBM
Copy link

AlanBM commented Dec 16, 2016

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.

@trknz
Copy link
Author

trknz commented Dec 17, 2016

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.

@watou
Copy link

watou commented Dec 21, 2016

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)

@watou
Copy link

watou commented Dec 22, 2016

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!

@jeffreyzh
Copy link

waiting for solutions.

@kuchaguangjie
Copy link

kuchaguangjie commented Jan 12, 2018

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

@centic9
Copy link

centic9 commented Jan 18, 2018

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.

Catfriend1 added a commit to Catfriend1/syncthing-android that referenced this issue Jan 26, 2019
Catfriend1 pushed a commit to Catfriend1/syncthing-android that referenced this issue Jan 27, 2019
* WIP

* Revert "WIP"

This reverts commit 98b34c4.

* WIP

* Revert "WIP"

This reverts commit 3b9fc96.

* Add de/serializer for MinDiskFree

* Move MinDiskFree out of Folder

* Move MinDiskFree out of Folder (2)

* Revert "Move MinDiskFree out of Folder (2)"

This reverts commit 65f87db.

* Revert "Move MinDiskFree out of Folder"

This reverts commit b71350b.

* Revert "Add de/serializer for MinDiskFree"

This reverts commit 5827426.

* RestApi: Add MinDiskFreeSerializer, MinDiskFreeDeserializer

* Revert "RestApi: Add MinDiskFreeSerializer, MinDiskFreeDeserializer"

This reverts commit 3922f24.

* Test

* Revert "Test"

This reverts commit 3550095.

* FolderActivity/DeviceActivity: Fix restApi unavailable in onCreate()

* Model/Folder#MinDiskFree: Initialize members (fixes #277)

* ConfigXml#getFolders: Add MinDiskFree (fixes #277)

* ConfigXml: Write back minDiskFree (fixes #277)

* Ignore notices about updating gradle dependencies

* ConfigXml: Make number parsing more safe

* FolderActivity#initFolder: Add new Folder.MinDiskFree

* Handle minDiskFree.value as String instead of float

* Revert "Handle minDiskFree.value as String instead of float"

This reverts commit 0552cfc.

* WIP

* Revert "WIP"

This reverts commit 0a3df91.

* RestApi: Avoid creating duplicate Gson() instances

* Model/Folder: Use Integer instead of Float

See gson glitch:
google/gson#1290
google/gson#968

* Try MinDiskFree.value as Long instead of Integer

* Revert "Try MinDiskFree.value as Long instead of Integer"

This reverts commit d358862.

* Revert "Model/Folder: Use Integer instead of Float"

This reverts commit ca3931b.

* Update model/Options: MinHomeDiskFree (fixes #277)
@JuanchiFraga
Copy link

Any idea how to fix it?

@RamakrishnanArun
Copy link

It's been five years, any hope of having a way to choose scientific notation vs. non-scientific notation?

@RamakrishnanArun
Copy link

OK looks like I found the way to do it by using BigDecimal. I have incoming data as a float, then convert to BigDecimal and then to the JsonObject and then toString.

public class JsonPrecisionTest {
    public static void main(String[] args) {
      System.out.println(createPayload(176.66f));
      System.out.println(createPayload(17326.237f));
    }

    public static String createPayload(float price) {
      BigDecimal bdPrice = new BigDecimal(price, new MathContext(countDigits(price)));
      JsonObject json = new JsonObject();
      json.addProperty("price", bdPrice);
      return json.toString();
    }

    public static int countDigits(Float number) {
      String str = number.toString();
      int len = str.length();
      if (str.contains(".")) {
        len --;
      }
      return len;
    }
}

and the output looks like this

{"price":176.66}
{"price":17326.236}

@yotamss
Copy link

yotamss commented Mar 5, 2021

A solution(?) for getting the long version of the number:
Lets assume my Double variable is named dNum (Double dNum = jsonMapper.get...).
dNum.longValue() would retrieve the correct number.

@eamonnmcmanus
Copy link
Member

Fixed by #1290.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests