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

前端 未结 7 2116
予麋鹿
予麋鹿 2020-11-29 06:14

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, 506         


        
相关标签:
7条回答
  • 2020-11-29 06:18

    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);
    
    0 讨论(0)
  • 2020-11-29 06:24

    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" ] }
    
    0 讨论(0)
  • 2020-11-29 06:26

    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
    
    0 讨论(0)
  • 2020-11-29 06:27

    I did not find a solution to my problem of gson formatting numbers ending in 0 to scientific notation. I instead used a work-around to convert this scientific notation into a double that I formatted with commas. "value" is the json string.

      private String formatNumber(String value) { 
        double dValue = Double.parseDouble(value);
        String pattern = "#,###";
        DecimalFormat formatter = new DecimalFormat(pattern);
        String newNumber = formatter.format(dValue);
    
                return newNumber;
    }
    

    This doesn't answer the question asked but is an added step to work-around the problem to display the numbers as required by the system.

    0 讨论(0)
  • 2020-11-29 06:28

    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]}
    
    0 讨论(0)
  • 2020-11-29 06:30

    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.

    0 讨论(0)
提交回复
热议问题