我正在使用Jackson
和Spring 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
“似乎”正常工作。但是,任何人都可以提出任何其他更简单(或更标准)的方法。
答案 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中,并摆脱自定义序列化。