3

As per JSON specification, I am aware that leading zeroes are not allowed in integers in JSON. But as per Jackson documentation, there is a property in Jackson library i.e. ALLOW_NUMERIC_LEADING_ZEROS which when enabled, does not throw exceptions when leading zeroes are found.

I enabled the property ALLOW_NUMERIC_LEADING_ZEROS by setting following property and still I am getting error: Leading zeroes not allowed.

spring.jackson.parser.ALLOW_NUMERIC_LEADING_ZEROS=true

Relevant Logs:

Caused by: com.fasterxml.jackson.core.JsonParseException: Invalid numeric value: Leading zeroes not allowed
at [Source: (PushbackInputStream); line: 8, column: 17]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1804) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:663) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.core.base.ParserMinimalBase.reportInvalidNumber(ParserMinimalBase.java:539) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._verifyNoLeadingZeroes(UTF8StreamJsonParser.java:1489) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._parsePosNumber(UTF8StreamJsonParser.java:1341) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextFieldName(UTF8StreamJsonParser.java:1025) ~[jackson-core-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:376) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001) ~[jackson-databind-2.9.4.jar:2.9.4]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072) ~[jackson-databind-2.9.4.jar:2.9.4]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:235) ~[spring-web-5.0.4.RELEASE.jar:5.0.4.RELEASE]
    ... 63 more

I verified whether the property ALLOW_NUMERIC_LEADING_ZEROS has been enabled or not by executing following code:

@Autowired
private ObjectMapper objectMapper;

@PostMapping(path = "random_path", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> fun123( @RequestBody RandomClass obj) throws Exception {

    log.info(" isEnabled = " + objectMapper.getFactory().isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));
    log.info(" isEnabled = " + objectMapper.isEnabled(JsonParser.Feature.ALLOW_NUMERIC_LEADING_ZEROS));

    /*
        When correct request is given i.e. no leading zeroes in json body , then this function is successfully executed and
        output is true for above two statements  => i.e. feature 'ALLOW_NUMERIC_LEADING_ZEROS' is enabled.

        When leading zeroes are present in json request body, this function is not executed as an exception 'HttpMessageNotReadableException'
        is generated with error message 'Invalid numeric value: Leading zeroes not allowed'
    */

  ....
}

As per code of UTF8StreamJsonParser.java , when this property is enabled the exception should not have occurred, but I am not sure why this is happening !! Any idea what can be the reason behind this ?

Relevant code from UTF8StreamJsonParser.java :

/**
 * Method called when we have seen one zero, and want to ensure
 * it is not followed by another
 */
private final int _verifyNoLeadingZeroes() throws IOException
{
    // Ok to have plain "0"
    if (_inputPtr >= _inputEnd && !_loadMore()) {
        return INT_0;
    }
    int ch = _inputBuffer[_inputPtr] & 0xFF;
    // if not followed by a number (probably '.'); return zero as is, to be included
    if (ch < INT_0 || ch > INT_9) {
        return INT_0;
    }
    // [JACKSON-358]: we may want to allow them, after all...
    if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
        reportInvalidNumber("Leading zeroes not allowed");
    }

    ...
}

Jackson Library Version Used : 2.9.4

user
  • 383
  • 1
  • 5
  • 20

4 Answers4

1

Simply put following property on your application.properties file

spring.jackson.parser.allow-numeric-leading-zeros=true

You can set jackson as default converter by following property if not set default

spring.http.converters.preferred-json-mapper=jackson
Emdadul Sawon
  • 5,730
  • 3
  • 45
  • 48
0

This is most likely due to ObjectMapper that Spring endpoint uses being configured different from mapper being injected into field. Why this is I can't say -- maybe Spring users list could help.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 2
    Thanks for looking at this. You are correct that injected mapper used above is different from the one used by Spring MVC internally. I verified that by overriding function `configureMessageConverters` of class `WebMvcConfigurationSupport` and checking the converters that it adds by default. I am also not sure why this was happening ! I had to enable the property by accessing the converter in the function `configureMessageConverters`. – user Apr 26 '19 at 10:02
0

MappingJackson2HttpMessageConverter by default uses Jackson2ObjectMapperBuilder class to build new instance of ObjectMapper class. To override and use ObjectMapper from container we need to override JSON converter:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

@Configuration
public class JacksonMvcConfiguration extends WebMvcConfigurationSupport {

    @Autowired
    private ObjectMapper objectMapper;

    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper);

        return converter;
    }

    @Override
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
        super.configureMessageConverters(converters);
    }
}

See Customizing HttpMessageConverters with Spring Boot and Spring MVC. Since now you should be able to parse numbers with leading zeros.

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
  • 1
    Well, I wanted to use the converter being used by Spring MVC, not the custom converter. So, I overrided the function `configureMessageConverters` (like you have done above), added the default converters by calling one of the internal functions and enabled the property by accessing the list `converters`. – user Apr 26 '19 at 10:06
  • @user, I tested my example using `Spring Boot`. It looks like `MappingJackson2HttpMessageConverter` does not use by default `ObjectMapper` created by container with updated configuration. We need to explicitly configure it for `MVC` converters. If you think my answer helped you somehow, upvote it. – Michał Ziober Apr 26 '19 at 10:10
  • I observed that the problem with explicitly configuring converters by overriding the `configuration` classes is that `Spring MVC` no longer does the auto configuration during initialisation. One may need to take care of all that. For example: In above (your) code, all the other default converters won't be added to the list. – user Apr 26 '19 at 10:24
  • @user, see this answer [How to configure MappingJacksonHttpMessageConverter while using spring annotation-based configuration?](https://stackoverflow.com/questions/10650196/how-to-configure-mappingjacksonhttpmessageconverter-while-using-spring-annotatio#10650452) and note "call to addDefaultHttpMessageConverters()". – Michał Ziober Apr 26 '19 at 10:59
0

Just a note for people that are having that issue and are looking for a newer working solution:

  1. Import latest version of fasterxml jackson in maven (2.11.0 as of today):

    <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.11.0</version> </dependency>

  2. Create the mapper object:

    ObjectMapper objectMapper = new ObjectMapper();

  3. Allow the leading zeros for numbers (the not deprecated version):

    objectMapper.enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS.mappedFeature());

used imports:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.json.JsonReadFeature;

Keep in mind this will trim the leading 0s. If you want to keep them then your json value shouldn't be a numeric.