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

2nc8po8w  于 2022-11-06  发布在  其他
关注(0)|答案(9)|浏览(267)

我需要将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

jdzmm42g

jdzmm42g1#

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

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

这将产生类似以下的结果:

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

kdfy810k2#

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

gdx19jrr3#

我遇到过类似的问题,它不仅将整数转换为双精度,而且实际上会丢失某些长数字的精度,如related question中所述。
我跟踪了ObjectTypeAdapterread方法的转换,具体来说:

case NUMBER:
  return in.nextDouble();

也许可以为Object插入一个修改过的TypeAdapter,但我无法让它工作,所以我只是将read方法(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);

我希望Gson默认这样做。
然后,我将其他连接部分放入一个helper方法中,如下所示:

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);
    }
}

现在我调用parse(r),而不是new Gson().fromJson(r, Object.class)
这对我来说很好,因为我希望能够解析任何结构的json数据,但是如果你有一个特定的目标类,你可能只需要在该类的成员中消除Object的出现。

a6b3iqyw

a6b3iqyw4#

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

行为:

  • gson

对于没有小数部分的数字,Gson会将其转换为Double

  • Jackson

对于没有小数部分的数字,Jackson会将其转换为IntegerLong,具体取决于数字的大小。

可能的解决方案:

  • Gson的返回值从Double显式转换为Long
  • 请改用Jackson

我更喜欢这个。

代码-Jackson的测试
分析数字测试.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;
    }
    }
}

数字.json:

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

如何运行:

  • 测试用例基于JacksonTestNG
  • numbers.json放在与ParseNumberTest.java相同的封装中。
  • 作为testng测试运行,然后它将打印解析结果的类型和值。
    输出:
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
dwthyt8l

dwthyt8l5#

不聪明,但仍然工作的方法是在开始和结束时加上“数字.然后在处理完毕后,将其删除。

icnyk63a

icnyk63a6#

我们可以使用下面的代码解决数字长:

Document doc = documentCursor.next();  

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

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

ws51t4hk7#

如果您需要将它们作为String,最好的解决方案是在转换之前强制属性用单引号引起来。
执行如下更改:

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

wyyhbhjk8#

data class Answer(
    val question: String,
    val value: Any
)

给定上面的value:Any属性,我发现Gson遇到1(而不是"1")的JSON值并且不能推断显而易见的事实是可疑的:1是一个Int。Gson会将整数值转换为双精度值1.0
如果上述value属性的类型为FloatDouble,Gson * 应该 * 仅将JSON值从1转换为1.0。当Gson遇到类型为Any的属性时,它应该(非常简单)从接收到的JSON值推断类型。更喜欢通过将传入的整数值强制转换为Double来实际损坏这些值,这会立即导致异常。
对于Gson解析器的这种特性,我找不到合理的解决方案。因此,我不得不在使用Gson后手动将所有双精度值转换回int值,或者为Gson实现我自己的通用自定义类型适配器。这两种选择都没有吸引力。

hmmo2u0o

hmmo2u0o9#

我没有找到解决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;
}

这并没有回答所提出的问题,而是一个附加步骤,用于解决该问题,以显示系统所需的数字。

相关问题