Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT to cover scalar type Objects #3941

Closed
GI-2023 opened this issue May 19, 2023 · 8 comments

Comments

@GI-2023
Copy link

GI-2023 commented May 19, 2023

Describe the bug
As stated in issue #1588 (further discussions in #1095 and #1106), there is currently no way to prevent mapping of an empty string ("") to Java "numbers" (java.lang.*: e.g. java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Double), - this always gets converted to null.

In other words, there is no difference between specifying (null) and ("") and there is no way to distinguish between the two.

Disabling MapperFeature.ALLOW_COERCION_OF_SCALARS is of no use, as it prevents valid numbers specified in a string to be processed (e.g. ""6"").

Version information
2.15.0

To Reproduce
import org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;

public class Test {

public static void main(String[] args) throws JsonMappingException, JsonProcessingException {
	Test.acceptEmptyStringAsNullObject();
	Test.allowCoersionOfScalars();
}

private static final void acceptEmptyStringAsNullObject() throws JsonMappingException, JsonProcessingException  {
    Jackson2ObjectMapperFactoryBean objectMapperFactory = new Jackson2ObjectMapperFactoryBean();
    objectMapperFactory.setFeaturesToDisable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    try {
        objectMapperFactory.afterPropertiesSet();
    } catch (Exception e) {
        throw new IllegalStateException("afterPropertiesSet() failed", e);
    }
    
	Integer i = objectMapperFactory.getObject().readValue("\"\"", Integer.class);
	System.out.println(i);			
}

private static final void allowCoersionOfScalars() throws JsonMappingException, JsonProcessingException  {
    Jackson2ObjectMapperFactoryBean objectMapperFactory = new Jackson2ObjectMapperFactoryBean();
    objectMapperFactory.setFeaturesToDisable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, MapperFeature.ALLOW_COERCION_OF_SCALARS);
    try {
        objectMapperFactory.afterPropertiesSet();
    } catch (Exception e) {
        throw new IllegalStateException("afterPropertiesSet() failed", e);
    }
    
	Integer i = objectMapperFactory.getObject().readValue("\"6\"", Integer.class);
	System.out.println(i);			
}

}

Current behavior

  1. "null" printed out to stdout
  2. Exception thrown in Test.allowCoersionOfScalars: Cannot coerce String value ("6") to java.lang.Integer value (but might if coercion using CoercionConfig was enabled)

Expected behavior

  1. Exception thrown in Test.acceptEmptyStringAsNullObject();
@GI-2023 GI-2023 added the to-evaluate Issue that has been received but not yet evaluated label May 19, 2023
@cowtowncoder
Copy link
Member

Actually there should be specific ways to prevent this with "Coercion Config" system, which is to fully replace coarse DeserializationFeatures such as ACCEPT_EMPTY_STRING_AS_NULL_OBJECT.

See, f.ex #3013 and later #3613.

@cowtowncoder cowtowncoder removed the to-evaluate Issue that has been received but not yet evaluated label May 19, 2023
@GI-2023
Copy link
Author

GI-2023 commented May 22, 2023

Thanks @cowtowncoder, but I am not 100% sure how do I apply this without writing my own deserializer (something I am trying to avoid).

In a nutshell, I'd like any conversion from String to "numbers" to be successful if the string is a valid number (that also includes "marginal" cases, where float to integers is also permitted, i.e. "6.5" to integer returns "6"), except if it is an empty string (""), in which case an exception should be thrown and not convert this to a null, which is what DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT does, but not for scalar objects (Integer, Double etc).

I don't see how that can be achieved with coercion config.

It goes without saying that null values that are specified explicitly (e.g. "numberProperty: null") should also be accepted.

@GI-2023
Copy link
Author

GI-2023 commented May 22, 2023

ObjectMapper mapper = objectMapperFactory.getObject(); mapper.coercionConfigFor(LogicalType.Float).setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail); mapper.coercionConfigFor(LogicalType.Integer).setCoercion(CoercionInputShape.EmptyString, CoercionAction.Fail);

The above pretty much did it, so no need to do anything special and it worked out of the box to `perfection!

@JooHyukKim
Copy link
Member

The above pretty much did it, so no need to do anything special and it worked out of the box to `perfection!

Should we close this is completed, @GI-2023 ?

@GI-2023
Copy link
Author

GI-2023 commented May 22, 2023

Yes please - the coersion config (something I wasn't aware of existed) works to perfection, so, yes, no need to do anything. Thank you for your support on this.

@JooHyukKim
Copy link
Member

Glad it all worked out @GI-2023 !👍🏻

Btw, you can close this issue yourself, since you opened it 👍🏻😄

@GI-2023
Copy link
Author

GI-2023 commented May 22, 2023

Ah, sure. apologies. Closing...

@GI-2023 GI-2023 closed this as completed May 22, 2023
@JooHyukKim
Copy link
Member

Ah, sure. apologies. Closing...

Oh no, I didn't mean anything that need apologies 🥹. I would've closed it for you if I had permission 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants