How to prevent Gson from converting a long number (a json string ) to scientific notation format?

I need to convert json string to java object and display it as a long. The json string is a fixed array of long numbers:

{numbers
[ 268627104, 485677888, 506884800 ] }

The code to convert works fine in all cases except for numbers ending in 0. It converts those to a scientific notation number format:

   public static Object fromJson(HttpResponse response, Class<?> classOf)
    throws IOException {
    InputStream instream = response.getResponseInputStream();                   

    Object obj = null;
    try {
        Reader reader = new InputStreamReader(instream, HTTP.UTF_8);

        Gson gson = new Gson();

        obj = gson.fromJson(reader, classOf); 

        Logger.d(TAG, "json --> "+gson.toJson(obj));
    } catch (UnsupportedEncodingException e) {
        Logger.e(TAG, "unsupported encoding", e);
    } catch (Exception e) {
        Logger.e(TAG, "json parsing error", e);
    }

    return obj;
}

The actual result: Java object : 268627104, 485677888, 5.068848E+8

Notice the last number is converted to a scientific notation format. Can anyone suggest what could be done to work around it or prevent it or undo it? I'm using Gson v1.7.1


Solution 1:

If serializing to a String is an option for you, you can configure GSON to do so with:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();

This will produce something like:

{numbers : [ "268627104", "485677888", "506884800" ] }

Solution 2:

Another work around is to use the JsonParser class instead. This will return the Gson object representations (JsonElement) rather than a user defined class, but avoids the problem of conversion to scientific notation.

import java.lang.reflect.Type;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

public class GsonTest
{
    public static void main(String[] args)
    {
        String json = "{numbers:[268627104,485677888,506884800]}";

        Gson gson = new Gson();
        Type type = new TypeToken<Map<String, Object>>(){}.getType();
        Map<String, Object> jsonMap = gson.fromJson(json, type);
        System.out.println("Gson output:");
        System.out.println(jsonMap);

        JsonParser jsonParser = new JsonParser();
        JsonElement jsonElement = jsonParser.parse(json);
        System.out.println("JsonParser output:");
        System.out.println(jsonElement);
    }
}

Code Output:

Gson output:  
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}  
JsonParser output:  
{"numbers":[268627104,485677888,506884800]}

Solution 3:

I had a similar problem, and it not only converts integers to double, but it actually loses precision for certain long numbers, as described in this related question.

I tracked down this conversion to ObjectTypeAdapter's read method, specifically:

case NUMBER:
  return in.nextDouble();

It may be possible to plug in a modified TypeAdapter for Object, but I couldn't get that to work, so instead I just copied the read method (Object read(JsonReader in)) to my own code and modified the above lines to this:

case NUMBER:
    final String s = in.nextString();
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException e) {
        // ignore
    }
    try {
        return Long.parseLong(s);
    } catch (NumberFormatException e) {
        // ignore
    }
    return Double.parseDouble(s);

I wish Gson did this by default..

Then I put the other connecting pieces in a helper method that looks something like this:

public static Object parse(final Reader r) {
    try (final JsonReader jr = new JsonReader(r)) {
        jr.setLenient(true);
        boolean empty = true;
        Object o = null;
        try {
            jr.peek();
            empty = false;
            o = read(jr);
        } catch (EOFException e) {
            if (!empty) {
                throw new JsonSyntaxException(e);
            }
        }
        if (o != null && jr.peek() != JsonToken.END_DOCUMENT) {
            throw new JsonIOException("JSON document was not fully consumed.");
        }
        return o;
    } catch (IOException e) {
        throw new JsonIOException(e);
    }
}

So now instead of new Gson().fromJson(r, Object.class), I call parse(r).

This works well for me because I want to be able to parse json data with any structure, but if you have a particular class you're targeting, you probably just need to eliminate occurrences of Object within that class's members.