Description
Search before asking
- I searched in the issues and found nothing similar.
Describe the bug
I have a custom NullModule extends SimpleModule that installs serializer and deserializer modifiers:
public NullModule() {
setSerializerModifier(new NullSerializerModifier());
setDeserializerModifier(new NullDeserializerModifier());
}
the NullSerializerModifier wraps each JsonSerializer with a subclass of StdDelegatingSerializer using a Converter that may return null for some special non-null instances of values to be serialized. I use this together with serializationInclusion(JsonInclude.Include.NON_NULL)
configuration of ObjectMapper to suppress serialization of bean attributes with null values, but still allow selective serialization of JSON null values for attributes that are assigned these special non-null instancess (an alternative to JsonNullable wrapper class from openapi-tools but without ugly wrapper class). This works perfectly but recently I found a problem since I wanted to serialize a special instance of a type that represents a root of hierarchy of types for the first time. It works correctly for types that don't represent a hierarchy of types since in this case the following StdDelegatingSerializer method is invoked:
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException
{
Object delegateValue = convertValue(value);
// should we accept nulls?
if (delegateValue == null) {
provider.defaultSerializeNull(gen);
return;
}
// 02-Apr-2015, tatu: As per [databind#731] may need to do dynamic lookup
JsonSerializer<Object> ser = _delegateSerializer;
if (ser == null) {
ser = _findSerializer(delegateValue, provider);
}
ser.serialize(delegateValue, gen, provider);
}
As you can see, the value
to be serialized is 1st converted to delegateValue
via provided Converter and then a special case is considered when this converted value is null
. In that case, null serialized value is emitted.
But in case the type of serialized value represents a hierarchy of types, the other StdDelegatingSerializer method is invoked:
public void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException
{
// 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now,
// let's give it a chance?
Object delegateValue = convertValue(value);
JsonSerializer<Object> ser = _delegateSerializer;
if (ser == null) {
ser = _findSerializer(value, provider);
}
ser.serializeWithType(delegateValue, gen, provider, typeSer);
}
This method, OTOH, does not consider a special case that delegateValue
might be null which causes a NullPointerException to be thrown later when this null value is attempted to be used for extracting the type ID from.
Currently my work-around is to override method serializeWithType in my custom StdDelegatingSerializer subclass and reimplement it with this special-case check inserted exactly at the same place as in the serialize method above.
Would you consider this a bug?
Regards, Peter
Version Information
2.17.0
Reproduction
Can be reproduced by running a unit test provided in the fix without the actual fix applied. The following is printed:
[ERROR] com.fasterxml.jackson.databind.ser.TestCustomSerializers.testIssue4575 -- Time elapsed: 0.005 s <<< ERROR!
com.fasterxml.jackson.databind.JsonMappingException: Cannot invoke "Object.getClass()" because "value" is null
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._wrapAsIOE(DefaultSerializerProvider.java:531)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:504)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:341)
at com.fasterxml.jackson.databind.ObjectMapper._writeValueAndClose(ObjectMapper.java:4811)
at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:4052)
at com.fasterxml.jackson.databind.ser.TestCustomSerializers.testIssue4575(TestCustomSerializers.java:397)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: java.lang.NullPointerException: Cannot invoke "Object.getClass()" because "value" is null
at com.fasterxml.jackson.databind.jsontype.impl.TypeNameIdResolver.idFromValue(TypeNameIdResolver.java:115)
at com.fasterxml.jackson.databind.jsontype.impl.TypeSerializerBase.idFromValue(TypeSerializerBase.java:92)
at com.fasterxml.jackson.databind.jsontype.impl.TypeSerializerBase._generateTypeId(TypeSerializerBase.java:77)
at com.fasterxml.jackson.databind.jsontype.impl.TypeSerializerBase.writeTypePrefix(TypeSerializerBase.java:45)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeWithType(BeanSerializerBase.java:648)
at com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer.serializeWithType(StdDelegatingSerializer.java:186)
at com.fasterxml.jackson.databind.ser.impl.TypeWrappedSerializer.serialize(TypeWrappedSerializer.java:32)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:502)
... 7 more
Expected behavior
I would expect the StdDelegatingSerializer to behave consistently for types that do and types that don't represent a type hierarchy.
Additional context
No response