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

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

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

 "amount":459.99999999999994 

我已经尝试过使用Spring 3的@NumberFormat注释,但是没有在这个方向上取得成功。 看起来其他人也有问题: 在这里将JSON绑定到JavaBean propertiesenter链接描述时,MappingJacksonHttpMessageConverter的ObjectMapper不使用ConversionService 。

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

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

和序列化器实现:

 public class CustomDoubleSerializer extends JsonSerializer { @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 “似乎”工作。 但是,任何人都可以建议任何其他更简单(或更标准)的方法。

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

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

这是完整的代码。

Product.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 + "]"; } } 

JavaToJSON.java

 package com; import java.io.File; import java.io.IOException; import org.codehaus.jackson.JsonGenerationException; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.ObjectMapper; public class JavaToJSON { public static void main(String[] args){ ObjectMapper mapper = new ObjectMapper(); try { Product product = new Product(); product.setName("TestProduct"); product.setAmount(Double.valueOf("459.99999999999994")); // Convert product to JSON and write to file mapper.writeValue(new File("d:\\user.json"), product); // display to console System.out.println(product); } catch (JsonGenerationException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 

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

希望这可以帮助。

我知道Double不应该用作货币金额。 但是,这不是我的代码。

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

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

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

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

关于上面所说的,我只想修一些东西,这样人们就不会像我那样浪费时间。 一个人应该实际使用

 BigDecimal.valueOf(amount).xxx 

代替

 new BigDecimal(amount).xxx 

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

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

 Math.round(value*10)/10.0 

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