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

时间:2012-07-20 20:08:20

标签: java json gson

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

{numbers
[ 268627104, 485677888, 506884800 ] }

转换代码在所有情况下都能正常工作,但以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;
}

实际结果: Java对象:268627104,485677888,5.068848E + 8

请注意,最后一个号码将转换为科学记数法格式。任何人都可以建议可以做些什么来解决它或阻止或撤消它?我正在使用Gson v1.7.1

8 个答案:

答案 0 :(得分:16)

如果您可以选择序列化为String,则可以使用以下命令配置GSON:

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

这会产生类似的东西:

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

答案 1 :(得分:8)

另一种解决方法是使用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);
    }
}

代码输出:

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

答案 2 :(得分:5)

我遇到了类似的问题,它不仅将整数转换为double,而且实际上对于某些长数字也会失去精确度,如related question中所述。

我将此转换追溯到ObjectTypeAdapter的{​​{1}}方法,具体为:

read

可以为case NUMBER: return in.nextDouble(); 插入修改后的TypeAdapter,但我无法使其正常工作,因此我只是复制了Object方法({ {1}})到我自己的代码并修改上面的行:

read

我希望Gson默认这样做。

然后我把其他连接件放在一个看起来像这样的辅助方法中:

Object read(JsonReader in)

现在我代替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); 而不是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); } }

这对我很有用,因为我希望能够使用任何结构解析json数据,但是如果你有一个特定的类,那么你可能只需要消除new Gson().fromJson(r, Object.class)内的parse(r)次出现该班的成员。

答案 3 :(得分:2)

得到同样的问题,经过一些调查后我发现了。

行为:

  • GSON
    对于没有小数部分的数字,Gson会将其转换为Double
  • 杰克逊
    对于没有小数部分的数字,Jackson会将其转换为IntegerLong,具体取决于数字的大小。

可能的解决方案:

  • 明确地将Gson的返回值从Double转换为Long
  • 请改用Jackson 我更喜欢这个。

代码 - 测试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
    ]
}

如何运行

  • 测试用例基于Jackson&amp; TestNG
  • numbers.json放在与ParseNumberTest.java相同的包裹中。
  • 以testng测试运行,然后打印类型&amp;解析结果的值。

<强>输出:

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

答案 4 :(得分:0)

不聪明,但仍然工作的方法是在数字的开头和结尾添加“。然后在处理完成后删除它。

答案 5 :(得分:0)

我们可以将以下代码解决方案用于数字Long:

Document doc = documentCursor.next();  

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

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

答案 6 :(得分:0)

如果您需要将它们作为字符串,最好的解决方案是在进行转换之前强制使用单引号引用属性。

做这样的改变:

String readerAsString = convertReaderToString(reader);
readerAsString = readerAsString.toString().replace("=", "='");
readerAsString = readerAsString.toString().replace(",", "',");
readerAsString = readerAsString.toString().replace("}]", "'}]");

答案 7 :(得分:-1)

我没有找到解决我的gson格式化数字以0结尾的问题的解决方案。我改为使用解决方法将这种科学记数法转换为用逗号格式化的双精度数。 “value”是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;
}

这不能回答问题,但是可以解决问题,以显示系统所需的数字。