如何防止Gson将一个长数字(json字符串)转换为科学的符号格式?

时间:2021-04-28 10:21: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:

我需要将json字符串转换为java对象,并将其显示为一个long。json字符串是一个固定的长数字数组:

{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:

除在0结尾的数字外,转换代码在所有情况下都适用。它将这些数字转换为科学的符号数字格式:

   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

实际结果:Java对象: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

注意最后一个数字转换为科学的符号格式。有没有人能建议我们可以做些什么来解决它,或者阻止它或者撤销它?我用Gson v1.7.1

6 个解决方案

#1


15  

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

如果序列化到一个字符串是您的一个选项,您可以配置GSON这样做:

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

This will produce something like:

这将产生一些类似的东西:

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

#2


8  

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.

另一个工作是使用JsonParser类。这将返回Gson对象表示(JsonElement),而不是用户定义的类,但避免了转换为科学符号的问题。

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]}

#3


1  

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.

我有一个类似的问题,它不仅将整数转换为double,而且它实际上会丢失某些长数字的精度,正如在这个相关的问题中所描述的那样。

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

我找到了这个转换到ObjectTypeAdapter的读取方法,具体如下:

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:

可以将修改后的TypeAdapter插入对象中,但是我不能让它工作,所以我只是将read方法(对象读(JsonReader))复制到我自己的代码中,并修改了上面的代码行:

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..

我希望Gson在默认情况下这样做。

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).

所以现在不是新的Gson(). fromjson (r, Object.class),我调用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.

这对我来说很有用,因为我希望能够解析json数据的任何结构,但是如果您有一个特定的类,那么您可能只需要在该类的成员中消除对象的出现。

#4


1  

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,
  • Gson的数字没有小数部分,Gson会把它转换成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.
  • 显式地将Gson的返回值从Double转换为Long。
  • Use Jackson instead.
    I prefer this.
  • 而是使用杰克逊。我更喜欢这个。

Code - test for Jackson

杰克逊的代码测试。

ParseNumberTest.java:

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:

numbers.json:

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

How to run:

如何运行:

  • The test case is based on Jackson & TestNG.
  • 测试用例基于Jackson & TestNG。
  • Put numbers.json at the same package as ParseNumberTest.java.
  • 把数字。与ParseNumberTest.java相同的包中的json。
  • Run as testng test, then it would print type & value of the parse result.
  • 作为testng测试,它将打印解析结果的类型和值。

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

#5


0  

Not smart, but still working method is to add " at the start and at the end of the number. Then after processing is finished, delete it.

不聪明,但仍在工作的方法是增加“在开始和结束时的数字。”处理完成后,删除它。

#6


-2  

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.

我没有找到一个解决我的gson格式的问题的解决方案,以0到科学的符号结尾。相反,我使用了一种方法,将这种科学符号转换成我用逗号进行格式化的两倍。“值”是json字符串。

  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.

这并没有回答问题,而是增加了解决问题的步骤,以显示系统需要的数字。

#1


15  

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

如果序列化到一个字符串是您的一个选项,您可以配置GSON这样做:

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

This will produce something like:

这将产生一些类似的东西:

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

#2


8  

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.

另一个工作是使用JsonParser类。这将返回Gson对象表示(JsonElement),而不是用户定义的类,但避免了转换为科学符号的问题。

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]}

#3


1  

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.

我有一个类似的问题,它不仅将整数转换为double,而且它实际上会丢失某些长数字的精度,正如在这个相关的问题中所描述的那样。

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

我找到了这个转换到ObjectTypeAdapter的读取方法,具体如下:

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:

可以将修改后的TypeAdapter插入对象中,但是我不能让它工作,所以我只是将read方法(对象读(JsonReader))复制到我自己的代码中,并修改了上面的代码行:

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..

我希望Gson在默认情况下这样做。

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).

所以现在不是新的Gson(). fromjson (r, Object.class),我调用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.

这对我来说很有用,因为我希望能够解析json数据的任何结构,但是如果您有一个特定的类,那么您可能只需要在该类的成员中消除对象的出现。

#4


1  

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,
  • Gson的数字没有小数部分,Gson会把它转换成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.
  • 显式地将Gson的返回值从Double转换为Long。
  • Use Jackson instead.
    I prefer this.
  • 而是使用杰克逊。我更喜欢这个。

Code - test for Jackson

杰克逊的代码测试。

ParseNumberTest.java:

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:

numbers.json:

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

How to run:

如何运行:

  • The test case is based on Jackson & TestNG.
  • 测试用例基于Jackson & TestNG。
  • Put numbers.json at the same package as ParseNumberTest.java.
  • 把数字。与ParseNumberTest.java相同的包中的json。
  • Run as testng test, then it would print type & value of the parse result.
  • 作为testng测试,它将打印解析结果的类型和值。

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

#5


0  

Not smart, but still working method is to add " at the start and at the end of the number. Then after processing is finished, delete it.

不聪明,但仍在工作的方法是增加“在开始和结束时的数字。”处理完成后,删除它。

#6


-2  

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.

我没有找到一个解决我的gson格式的问题的解决方案,以0到科学的符号结尾。相反,我使用了一种方法,将这种科学符号转换成我用逗号进行格式化的两倍。“值”是json字符串。

  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.

这并没有回答问题,而是增加了解决问题的步骤,以显示系统需要的数字。