9

I'm having a problem in which jackson is deserializing numeric values into arbitrary types which I cannot predict. For example, if someone passes the value "14", jackson will instantiate it as an Integer. However, if someone passes the value "14.01" then jackson will instantiate it as a Double. This is a problem because I have a custom deserializer (@JsonCreator) which is throwing exceptions since I cannot predictable cast the field into a BigDecimal. Ideally jackson would just turn everything into a BigDecimal.

I found a post which suggests that Jackson might be capable of doing something like this. Deserialize JSON into a generic map, forcing all JSON floats into Decimal or String, in Jackson

However, I can't figure out how to access the mapper object hidden inside Spring Boot in order to run the appropriate method mapper.enable().

Here is a snippet of the deserializer:

@JsonCreator
public OptionTransaction(Map<String,Object> jsonObj){  
    Map<String,Object> jsonOption = (Map<String, Object>) jsonObj.get("option");

    Map<String,Object> optionPriceObj = (Map<String, Object>) jsonOption.get("price");
    BigDecimal optionValue = new BigDecimal((Double) optionPriceObj.get("value"));

As you can see above, that Double cast is a problem because jackson is sometimes not feeding in doubles. Does anyone know an easy way to get jackson to either always output BigDecimals, or even just strings?

UPDATE:

As far as getting doubles converted to BigDecimal, I accomplished this by modifying application.properties in the following way:

# ===============================
# = DESERIALIZATION CUSTOMIZATION
# ===============================
spring.jackson.deserialization.USE_BIG_DECIMAL_FOR_FLOATS=true
Community
  • 1
  • 1
melchoir55
  • 6,842
  • 7
  • 60
  • 106
  • Why do you need access to the Spring mapper, why don't you just create your own? – Essex Boy Oct 24 '16 at 09:04
  • The answer to my question could include that, but that wouldn't fully answer the question. If I were to create my own mapper, how would I get spring boot to start using it? – melchoir55 Oct 26 '16 at 04:13

2 Answers2

6
@JsonCreator
public OptionTransaction(Map<String,Object> jsonObj){  
    Map<String,Object> jsonOption = (Map<String, Object>) jsonObj.get("option");

    Map<String,Object> optionPriceObj = (Map<String, Object>) jsonOption.get("price");
    BigDecimal optionValue = new BigDecimal((Double) optionPriceObj.get("value"));
}

..

ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);

OptionTransaction transaction = mapper.readValue(jsonString, OptionTransaction.class);

https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

kuhajeyan
  • 10,727
  • 10
  • 46
  • 71
  • I think to mark the question as answered this should also describe how to get sping boot to start using this mapper for future deserialization attempts. Is that even possible? For example, any endpoint using `@RequestBody Object obj` should be deserialized with this mapper. – melchoir55 Oct 26 '16 at 04:18
  • 1
    How can we set custom scale and precision in this deserializer ? – Raj Saraogi Apr 27 '22 at 06:34
  • is there a way to set this with class level annotations? – Joaquín L. Robles Dec 27 '22 at 14:39
0
@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder
                .modules(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES), new JavaTimeModule())
                .deserializers( new NumberDeserializers.BigDecimalDeserializer())
                .featuresToEnable(MapperFeature.AUTO_DETECT_CREATORS)
                .build();
    }
}
PlickPlick
  • 197
  • 15