Serialize a Double to 2 decimal places using Jackson

Solution 1:

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.

Solution 2:

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.

Solution 3:

Regarding what was stated above, I just wanted to fix a little something, so that people won't waste time on it as I did. One should actually use

BigDecimal.valueOf(amount).xxx

instead of

new BigDecimal(amount).xxx

and this is actually somehow critical. Because if you don't, your decimal amount will be messed up. This is a limitation of floating point representation, as stated here.