Search code examples
javajsonserializationjacksonjsr354

Customizing Databind Json serialization format of MonetaryAmount JSR354 / moneta


I'm trying to Json Serialize a POJO with MonetaryAmount field as a string, but the resulting output does not follow the prescribed shape format.

// org.javamoney:moneta:1.1 
// com.fasterxml.jackson.core:jackson-annotations:2.7.0
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "CBNOT_CHARGEBACK_AMOUNT"
})
public class TestMonetaryAmountJsonSerialization {
        @JsonProperty("CBNOT_CHARGEBACK_AMOUNT")
        @NotNull
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "¤#,##0.00", locale = "en_US")
        private final MonetaryAmount chargebackAmount = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(12.50).create();

        private static final ObjectMapper mapper = new ObjectMapper();

        @Test
        public void testThis() throws JsonProcessingException{
            String json = mapper.writeValueAsString(this);
            System.out.println(json);
            Assert.assertEquals("{\"CBNOT_CHARGEBACK_AMOUNT\":\"¤12.50\"}",json);
        }

}

OUTPUT: {"CBNOT_CHARGEBACK_AMOUNT":{"currency":{"context":{"empty":false,"providerName":"java.util.Currency"},"defaultFractionDigits":2,"currencyCode":"USD","numericCode":840},"number":12.5,"factory":{"defaultMonetaryContext":{"precision":0,"fixedScale":false,"amountType":"org.javamoney.moneta.Money","maxScale":63,"empty":false,"providerName":null},"maxNumber":null,"minNumber":null,"amountType":"org.javamoney.moneta.Money","maximalMonetaryContext":{"precision":0,"fixedScale":false,"amountType":"org.javamoney.moneta.Money","maxScale":-1,"empty":false,"providerName":null}},"context":{"precision":0,"fixedScale":false,"amountType":"org.javamoney.moneta.Money","maxScale":63,"empty":false,"providerName":null},"numberStripped":12.5,"zero":false,"negative":false,"negativeOrZero":false,"positive":true,"positiveOrZero":true}}

Any ideas what am I doing wrong? I threw the kitchen sink in this code here, only for illustrative purposes and compact presentation.


Solution

  • JsonFormat is an annotation used in several (de)serializers defined by Jackson (e.g. DateTimeSerializerBase, NumberSerializers.Base and some other, full list here), it's not a general purpose mechanism turning any object into a string:

    Unlike most other Jackson annotations, annotation does not have specific universal interpretation: instead, effect depends on datatype of property being annotated (or more specifically, deserializer and serializer being used).

    Specifying it won't have any effect unless you create a custom serializer for MonetaryAmount or use one that makes use of this annotation (and also its pattern property), but if you create a custom serializer, chances are you won't need that level of flexibility as to specify different patterns for different fields and could just use a fixed MonetaryAmountFormat or build the necessary string from the MonetaryAmount object otherwise.

    For example

    @JsonInclude(JsonInclude.Include.NON_NULL)
    @JsonPropertyOrder({
            "CBNOT_CHARGEBACK_AMOUNT"
    })
    public class TestMonetaryAmountJsonSerialization {
        @JsonProperty("CBNOT_CHARGEBACK_AMOUNT")
        @NotNull
        private final MonetaryAmount chargebackAmount = Monetary.getDefaultAmountFactory().setCurrency("USD").setNumber(12.50).create();
    
        private static final ObjectMapper mapper = new ObjectMapper();
        static {
            SimpleModule monetaryModule = new SimpleModule();
            monetaryModule.addSerializer(MonetaryAmount.class, new MonetaryAmountSerializer());
            mapper.registerModule(monetaryModule);
        }
    
        @Test
        public void testThis() throws JsonProcessingException {
            String json = mapper.writeValueAsString(this);
            System.out.println(json);
            Assert.assertEquals("{\"CBNOT_CHARGEBACK_AMOUNT\":\"$12.50\"}", json);
        }
    
        public static class MonetaryAmountSerializer extends JsonSerializer<MonetaryAmount> {
            public void serialize(MonetaryAmount monetaryAmount,
                                  JsonGenerator jsonGenerator,
                                  SerializerProvider serializerProvider) throws IOException {
                StringBuilder sb = new StringBuilder();
                MonetaryAmountDecimalFormatBuilder
                        .of("¤#,##0.00").withCurrencyUnit(monetaryAmount.getCurrency()).build()
                        .print(sb, monetaryAmount);
                jsonGenerator.writeString(sb.toString());
            }
        }
    }