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

ObjectMapper.readValue("123", Void.TYPE) throws "should never occur" #2679

Closed
stolsvik opened this issue Apr 4, 2020 · 1 comment
Closed
Milestone

Comments

@stolsvik
Copy link

stolsvik commented Apr 4, 2020

objectMapper.readValue("123", Void.class) returns null.
objectMapper.readValue("123", Void.TYPE) throws JsonMappingException, with cause IllegalArgumentException, whose code comment reads "should never occur".
objectMapper.readValue("123", void.class) also throws, since void.class == Void.TYPE

I would argue that Void.TYPE and void.class should also return null, unconditionally.

.. basically, in the same vein as if specifying Void.class, you get null, and considering
objectMapper.readValue("123", Integer.TYPE) returns 123 - and that the only value a Void variable can have is null.

Cause Exception:

Caused by: java.lang.IllegalArgumentException: Internal error: can't find deserializer for void
	at com.fasterxml.jackson.databind.deser.std.NumberDeserializers.find(NumberDeserializers.java:109)
	at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findDefaultDeserializer(BasicDeserializerFactory.java:1845)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.findStdDeserializer(BeanDeserializerFactory.java:167)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:131)
...

The reason is that this NumberDeserializers first checks whether the class is primitive (yes), and then has checks for all of Integer.TYPE, Boolean.TYPE etc, but lacks one for Void.TYPE. It thus falls out of that "then" block, and hits the default "should never occur".

The reason why this hits me, is that I have a (de)serialization situation in my library where users must specify which type they need their incoming message as - but can also effectively say "I don't care". Up until now I've used Void.class for this, but have come to realize that this is actually not correct - as the Void class that holds the TYPE field is "just a random class". You want either void.class (lowercase 'v'), or "Void.TYPE" - these two are actually the same instance, as opposed to Void.class.

The Void.class actually works because there is special handling for this case in JdkDeserializers:

            if (rawType == Void.class) {
                return NullifyingDeserializer.instance;
            }

This does not catch the void.class (or Void.TYPE), and it is anyway too late, since the NumberDeserializers class already has crashed it.

Here's a small test:

public class TestVoid {
    public static void main(String[] args) throws JsonProcessingException {
        System.out.println("Void.class: "+System.identityHashCode(Void.class));
        System.out.println("void.class: "+System.identityHashCode(void.class));
        System.out.println("Void.TYPE: "+System.identityHashCode(Void.TYPE));
        System.out.println("Void.TYPE == void.class: "+(void.class == Void.TYPE));
        System.out.println("----");

        ObjectMapper ser = new ObjectMapper();
        System.out.println("Deserialized Void.class: "+ser.readValue("123", Void.class));
        System.out.println("Deserialized Integer.TYPE: "+ser.readValue("123", Integer.TYPE));
        System.out.println("Deserialized void.class: "+ser.readValue("123", void.class));
        System.out.println("Deserialized Void.TYPE: "+ser.readValue("123", Void.TYPE));
    }
}

Prints something like:

Void.class: 999966131
void.class: 1989780873
Void.TYPE: 1989780873
Void.TYPE == void.class: true
----
Deserialized Void.class: null
Deserialized Integer.TYPE: 123
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Internal error: can't find deserializer for void
 at [Source: (String)"123"; line: 1, column: 1]
@cowtowncoder
Copy link
Member

Sort of related to #2675, but I guess root value handling is probably bit easier.
I agree that it'd make sense to return null for both "primitive" void and Void.class.

@cowtowncoder cowtowncoder added this to the 2.10.4 milestone Apr 5, 2020
@cowtowncoder cowtowncoder changed the title ObjectMapper.readValue("123", Void.TYPE) throws "should never occur" ObjectMapper.readValue("123", Void.TYPE) throws "should never occur" Apr 5, 2020
@cowtowncoder cowtowncoder modified the milestone: 2.10.4 Apr 5, 2020
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

2 participants