在 Gson 双序列化中关闭科学记数法

Posted

技术标签:

【中文标题】在 Gson 双序列化中关闭科学记数法【英文标题】:switch off scientific notation in Gson double serialization 【发布时间】:2012-06-20 12:01:22 【问题描述】:

当我使用 Gson 序列化一个包含接近零的双精度值的对象时,它使用的是科学 E 符号:

"doublevaule":5.6E-4

如何告诉 Gson 生成

"doublevaule":0.00056

相反?我可以实现一个自定义的 JsonSerializer,但它返回一个 JsonElement。我将不得不返回一个 JsonPrimitive,其中包含一个无法控制其序列化方式的 double。

谢谢!

【问题讨论】:

为什么会有问题?科学记数法在 JSON 中有效,任何处理 JSON 的东西都应该能够正确解析(与未使用科学记数法完全相同的值)。 我不反对,你是对的 Joachim。我仍然希望我的 JSON 不包含科学记数法。这对 GSon 可行吗? 我有完全相同的问题,仅仅是因为我有一个无法处理指数的损坏的 JSON 消费者。我意识到真正的解决方法是对消费者进行排序,但有时这超出了我们的控制范围。因此,我认为这是一个合理(如果不寻常)的要求 【参考方案1】:

为什么不为Double 提供新的序列化程序? (您可能必须重写您的对象以使用Double 而不是double)。

然后在序列化器中,您可以转换为BigDecimal,并使用比例等。

例如

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() 
        @Override
        public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) 
            BigDecimal value = BigDecimal.valueOf(src);

            return new JsonPrimitive(value);
        
    );

    gson = gsonBuilder.create();

上面将呈现(比如)9.166666E-60.000009166666

【讨论】:

使用 Kotlin - 查看我的回答 ***.com/a/57254418/2826184【参考方案2】:

Brian Agnew's answer 的小改动:

public class DoubleJsonSerializer implements JsonSerializer<Double> 
    @Override
    public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) 
        BigDecimal value = BigDecimal.valueOf(src);
        try 
            value = new BigDecimal(value.toBigIntegerExact());
         catch (ArithmeticException e) 
            // ignore
        

        return new JsonPrimitive(value);
    

【讨论】:

【参考方案3】:

您可以尝试扩展 JsonWriter 并覆盖 the method value(double)

它似乎不是为了像这样修改而构建的(您几乎需要复制现有代码),但应该可以让它工作。

很遗憾,我认为没有其他原因会影响输出格式。

【讨论】:

这是正确的方法,但它可能需要大量重复的代码。 JsonWriter 不是为这种可扩展性而设计的,并且缺少必要的钩子。【参考方案4】:

Double 创建一个自定义序列化程序

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() 
    @Override
    public JsonElement serialize(Double originalValue, Type typeOf, JsonSerializationContext context) 
        BigDecimal bigValue = BigDecimal.valueOf(originalValue);

        return new JsonPrimitive(bigValue.toPlainString());
    
);

之前:“金额”:1.0E9

之后:“金额”:“1000000000”

不完全完美,因为它是 JSON 中的字符串。

【讨论】:

【参考方案5】:

GSON 内部使用 Number#toString,所以我们只需要创建一个 Number 的新实例:

.registerTypeAdapter(Double.class, new JsonSerializer<Double>() 
    @Override
    public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) 

        Number n = new Number() 

            @Override
            public long longValue() 
                return 0;
            

            @Override
            public int intValue() 
                return 0;
            

            @Override
            public float floatValue() 
                return 0;
            

            @Override
            public double doubleValue() 
                return 0;
            

            @Override
            public String toString() 
                return new BigDecimal(src).toPlainString();
            

        ;

        return new JsonPrimitive(n);
    
)

【讨论】:

【参考方案6】:

使用 Kotlin:

    val gsonBuilder = GsonBuilder()
    gsonBuilder.registerTypeAdapter(object: TypeToken<Double>() .type, object : JsonSerializer<Double> 
        override fun serialize(src: Double, typeOfSrc: Type, context: JsonSerializationContext): JsonElement 
            val value = BigDecimal.valueOf(src)
            return JsonPrimitive(value)
        
    )

    val gson = gsonBuilder.create()

【讨论】:

【参考方案7】:

由于某种原因,我无法按照许多人的建议使用 BigDecimal 的解决方案。所以我不得不这样写:

.registerTypeAdapter(object : TypeToken<Double>() .type, object : JsonSerializer<Double> 
    override fun serialize(src: Double, typeOfSrc: Type, context: JsonSerializationContext): JsonElement =
        JsonPrimitive(src.toBigDecimal().toPlainString().toBigDecimal())
)

【讨论】:

以上是关于在 Gson 双序列化中关闭科学记数法的主要内容,如果未能解决你的问题,请参考以下文章

在 Gnuplot 中关闭科学记数法

解决GSON转Long型变为科学计数法或整形变double的问题(自动转换成Double类型

以更易读的格式显示双精度科学记数法

在 Java 中使用 xstream 转换为 xml 时如何避免大双精度数的科学记数法

显示没有科学记数法的双变量的值

以科学记数法将小(1.0E-12)数字存储为双精度数