使用Jackson序列化一个Double到2的小数位

时间:2012-07-17 10:38:51

标签: java jackson

我正在使用JacksonSpring MVC来写出一些简单的对象JSON。其中一个对象具有amount属性,类型为Double。 (我知道Double不应该用作金额。但是,这不是我的代码。)

JSON输出中,我想将金额限制为2位小数。目前它显示为:     

"amount":459.99999999999994

我尝试过使用Spring 3的@NumberFormat注释,但在这方面没有取得成功。看起来其他人也有问题:MappingJacksonHttpMessageConverter's ObjectMapper does not use ConversionService when binding JSON to JavaBean propertiesenter link description here

另外,我尝试使用@JsonSerialize注释和自定义序列化程序 在模型中:     

@JsonSerialize(using = CustomDoubleSerializer.class)
public Double getAmount()

串行器实现:     

public class CustomDoubleSerializer extends JsonSerializer<Double> {
    @Override
    public void serialize(Double value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
        if (null == value) {
            //write the word 'null' if there's no value available
            jgen.writeNull();
        } else {
            final String pattern = ".##";
            //final String pattern = "###,###,##0.00";
            final DecimalFormat myFormatter = new DecimalFormat(pattern);
            final String output = myFormatter.format(value);
            jgen.writeNumber(output);
        }
    }
}

CustomDoubleSerializer“似乎”正常工作。但是,任何人都可以提出任何其他更简单(或更标准)的方法。

5 个答案:

答案 0 :(得分:4)

我的项目情况类似。我已将格式化代码添加到POJO的setter方法中。 DecimalFormatter,Math和其他类最终会使值四舍五入,但是,我的要求不是将值四舍五入,而是将显示限制为2位小数。

我重新创建了这个场景。 产品是POJO,其成员为length(myResp)。 JavaToJSON是一个将创建Product实例并将其转换为JSON的类。 Double amount中的setter setAmount将格式化为2位小数。

这是完整的代码。

Product.java

Product

JavaToJSON.java

package com;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Product {

    private String name;
    private Double amount;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getAmount() {
        return amount;
    }
    public void setAmount(Double amount) {
        BigDecimal bd = new BigDecimal(amount).setScale(2, RoundingMode.FLOOR);
        this.amount = bd.doubleValue();
    }

    @Override
    public String toString() {
        return "Product [name=" + name + ", amount=" + amount + "]";
    }
}

我没有积累足够的积分,所以我无法上传截图以显示输出。

希望这有帮助。

答案 1 :(得分:3)

  

我知道Double不应该用作金额。然而,   这不是我的代码。

确实,它不应该。 BigDecimal是存储货币金额的更好选择,因为它无损 并且可以更好地控制小数位。

因此对于那些可以控制代码的人来说,它可以像这样使用:

double amount = 111.222;
setAmount(new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP));

将序列化为111.22。无需自定义序列化程序。

答案 2 :(得分:1)

关于上面所述的内容,我只想修一些东西,以便人们不会像我那样浪费时间。实际上应该使用

BigDecimal.valueOf(amount).xxx

而不是

new BigDecimal(amount).xxx

这实际上是某种关键。因为如果你不这样做,你的小数将被搞砸。这是浮点表示的限制,如here所述。

答案 3 :(得分:0)

到目前为止,我所看到的最好的方法是创建一个自定义的序列化器和@JsonSerializer(using=NewClass.class)。想尝试使用@JsonFormat(pattern=".##")左右,但根据OP的评论可能无法正常工作(我认为格式化程序不赞成这样做)

查看此处:https://github.com/FasterXML/jackson-databind/issues/632

public class MoneyDeserializer extends JsonDeserializer<BigDecimal> {

    private NumberDeserializers.BigDecimalDeserializer delegate = NumberDeserializers.BigDecimalDeserializer.instance;

    @Override
    public BigDecimal deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        BigDecimal bd = delegate.deserialize(jp, ctxt);
        bd = bd.setScale(2, RoundingMode.HALF_UP);
        return bd;
    }    
}

BUT ,尽管编写起来更方便且代码更少,但是通常,确定字段的大小是业务逻辑的考虑,而不是(反序列化)的一部分。要清楚一点。杰克逊应该能够按原样传递数据。

答案 4 :(得分:-4)

请注意,459.99999999999994实际上是460,并且预计将以这种方式序列化。所以,你的逻辑应该比丢弃数字更棘手。 我可能会建议:

Math.round(value*10)/10.0

您可能希望将其置于setter中,并摆脱自定义序列化。