Подтвердить что ты не робот

Сериализовать Двойной до 2 знаков после запятой, используя Джексон

Я использую Jackson, с Spring MVC, чтобы выписать некоторые простые объекты как JSON. Один из объектов имеет свойство amount типа Double. (Я знаю, что Double не следует использовать в качестве денежной суммы. Однако это не мой код.)

В выводе JSON я хотел бы ограничить сумму до двух знаков после запятой. В настоящее время это показано как:   

"amount":459.99999999999994

Я пробовал использовать аннотацию Spring 3 @NumberFormat, но не добился успеха в этом направлении. Похоже, что у других тоже были проблемы: MappingJacksonHttpMessageConverter ObjectMapper не использует ConversionService при привязке описания JSON к JavaBean propertiesenter здесь.

Кроме того, я попытался использовать аннотацию @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 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 ". Однако может ли кто-нибудь предложить любой другой более простой (или более стандартный) способ сделать это.

4b9b3361

Ответ 1

У меня была аналогичная ситуация в моем проекте. Я добавил код форматирования в метод setter POJO. DecimalFormatter, Math и другие классы закончили округление значения, однако мое требование состояло не в том, чтобы округлить значение, а только для ограничения отображения до двух знаков после запятой.

Я воссоздал этот сценарий. Продукт представляет собой POJO, который имеет член Double amount. JavaToJSON - это класс, который создаст экземпляр Product и преобразует его в JSON. Установщик setAmount в Product позаботится о форматировании до 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();

        }
    }

}

У меня недостаточно накопленных баллов, поэтому я не могу загрузить скриншоты, чтобы показать вам результат.

Надеюсь, что это поможет.

Ответ 2

Я знаю, что Double не следует использовать в качестве денежной суммы. Однако, это не мой код.

В самом деле, этого не должно быть. BigDecimal - гораздо лучший выбор для хранения денежных сумм, поскольку он без потерь и обеспечивает больший контроль над запятыми.

Итак, для людей, которые имеют контроль над кодом, его можно использовать следующим образом:

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

Это будет сериализоваться как 111.22. Никаких специальных сериализаторов не требуется.

Ответ 3

Обратите внимание, что 459.99999999999994 эффективно 460 и, как ожидается, будет сериализоваться таким образом. Итак, ваша логика должна быть сложнее, чем просто отбрасывать цифры. Я мог бы предложить что-то вроде:

Math.round(value*10)/10.0

Возможно, вы захотите установить его в сеттер и избавиться от пользовательской сериализации.