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

Viewed 38650

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

9 Answers

Got the same issue, after some investigation here is what I found.

The behavior:

  • Gson
    For a number without fractional part, Gson would convert it as Double,
  • Jackson
    For a number without fractional part, Jackson would convert it as Integer or Long, depends on how large the number is.

Possible solutions:

  • Convert Gson's return value from Double to Long, explicitly.
  • Use Jackson instead.
    I prefer this.

Code - test for Jackson

ParseNumberTest.java:

import java.util.List;

import org.testng.Assert;
import org.testng.annotations.Test;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * test - jackson parse numbers,
 * 
 * @author eric
 * @date Jan 13, 2018 12:28:36 AM
 */
public class ParseNumberTest {
    @Test
    public void test() throws Exception {
    String jsonFn = "numbers.json";

    ObjectMapper mapper = new ObjectMapper();

    DummyData dd = mapper.readValue(this.getClass().getResourceAsStream(jsonFn), DummyData.class);
    for (Object data : dd.dataList) {
        System.out.printf("data type: %s, value: %s\n", data.getClass().getName(), data.toString());
        Assert.assertTrue(data.getClass() == Double.class || data.getClass() == Long.class || data.getClass() == Integer.class);

        System.out.printf("%s\n\n", "------------");
    }
    }

    static class DummyData {
    List<Object> dataList;

    public List<Object> getDataList() {
        return dataList;
    }

    public void setDataList(List<Object> dataList) {
        this.dataList = dataList;
    }
    }
}

numbers.json:

{
    "dataList": [
        150000000000,
        150778742934,
        150000,
        150000.0
    ]
}

How to run:

  • The test case is based on Jackson & TestNG.
  • Put numbers.json at the same package as ParseNumberTest.java.
  • Run as testng test, then it would print type & value of the parse result.

Output:

data type: java.lang.Long, value: 150000000000
------------

data type: java.lang.Long, value: 150778742934
------------

data type: java.lang.Integer, value: 150000
------------

data type: java.lang.Double, value: 150000.0
------------

PASSED: test

We can use the below code solution for number Long:

Document doc = documentCursor.next();  

JsonWriterSettings relaxed = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();  

CustomeObject obj = gson.fromJson(doc.toJson(relaxed), CustomeObject.class);

The best solution in case you need them as String was to force attributes to be quoted with a single quote before doing the conversion.

Do changes like this:

String readerAsString = convertReaderToString(reader);
readerAsString = readerAsString.toString().replace("=", "='");
readerAsString = readerAsString.toString().replace(",", "',");
readerAsString = readerAsString.toString().replace("}]", "'}]");
data class Answer(
    val question: String,
    val value: Any
)

Given the value:Any property above, I find it dubious that Gson encounters a JSON value of 1 (not "1") and cannot INFER the blatantly obvious truth: 1 is an Int. Instead, Gson converts the integer value to the double 1.0.

Gson should only convert a JSON value from 1 to 1.0 if the value property above was of type Float or Double. When Gson encounters a property whose type is Any, it should (quite simply) infer the type from the JSON value it receives. Unfortunately, it doesn't, preferring to actually corrupt incoming integer values by casting them to Double, which unsurprisingly immediately causes exceptions.

I can find no reasonable solution to this peculiarity of the Gson parser. As such, I'm forced to either manually convert all those double values back into int values after using Gson or implement my own generic custom type adapter for Gson. Neither of these options is at all appealing.

Related