I'm using Jackson
, with Spring MVC
, to write out some simple objects as JSON
. One of the objects, has an amount
property, of type Double
. (I know that Double
should not be used as a monetary amount. However, this is not my code.)
In the JSON
output, I'd like to restrict the amount to 2 decimal places. Currently it is shown as:
"amount":459.99999999999994
I've tried using Spring 3's @NumberFormat
annotation, but haven't had success in that direction. Looks like others had issues too: MappingJacksonHttpMessageConverter's ObjectMapper does not use ConversionService when binding JSON to JavaBean propertiesenter link description here.
Also, I tried using the @JsonSerialize
annotation, with a custom serializer.
In the model:
@JsonSerialize(using = CustomDoubleSerializer.class) public Double getAmount()
And serializer implementation:
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); } } }
The CustomDoubleSerializer
"appears" to work. However, can anyone suggest any other simpler (or more standard) way of doing this.
I know that
Double
should not be used as a monetary amount. However, this is not my code.
Indeed, it should not. BigDecimal
is a much better choice for storing monetary amounts because it is lossless and provides more control of the decimal places.
So for people who do have control over the code, it can be used like this:
double amount = 111.222; setAmount(new BigDecimal(amount).setScale(2, BigDecimal.ROUND_HALF_UP));
That will serialize as 111.22
. No custom serializers needed.
I had a similar situation in my project. I had added the formatting code to the setter method of the POJO. DecimalFormatter, Math and other classes ended up rounding off the value, however, my requirement was not to round off the value but only to limit display to 2 decimal places.
I recreated this scenario. Product is a POJO which has a member Double amount
. JavaToJSON is a class that will create an instance of Product and convert it to JSON. The setter setAmount
in Product
will take care of formatting to 2 decimal places.
Here is the complete code.
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(); } } }
I haven't accumulated enough points so I am not able to upload the screenshots to show you the output.
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With