Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize a Double to 2 decimal places using Jackson

Tags:

java

jackson

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.

like image 971
mlo55 Avatar asked Jul 17 '12 10:07

mlo55


2 Answers

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.

like image 88
rustyx Avatar answered Sep 20 '22 17:09

rustyx


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.

like image 33
Dinesh Chitlangia Avatar answered Sep 20 '22 17:09

Dinesh Chitlangia