-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Description
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]