Skip to content

Commit 577fe9c

Browse files
plevartcowtowncoder
authored andcommitted
StdDelegatingSerializer does not consider a Converter that may return null for a non-null input (#4576)
1 parent a01d01f commit 577fe9c

File tree

4 files changed

+98
-9
lines changed

4 files changed

+98
-9
lines changed

release-notes/CREDITS-2.x

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,3 +1774,8 @@ Oddbjørn Kvalsund (oddbjornkvalsund@github)
17741774
* Reported, contributed fix for #4430: Use `ReentrantLock` instead of `synchronized`
17751775
in `DeserializerCache` to avoid deadlock on pinning
17761776
(2.17.1)
1777+
1778+
Peter Levart (plevart@github)
1779+
* Reported, contributed fix for #4575: StdDelegatingSerializer does not consider
1780+
a Converter that may return null for a non-null input
1781+
(2.17.2)

release-notes/VERSION-2.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Project: jackson-databind
1212

1313
#4561: Issues using jackson-databind 2.17.1 with Reactor
1414
(reported by @wdallastella)
15+
#4575: StdDelegatingSerializer does not consider a Converter that may
16+
return null for a non-null input
17+
(reported, fix contributed by Peter L)
1518

1619
2.17.1 (04-May-2024)
1720

src/main/java/com/fasterxml/jackson/databind/ser/std/StdDelegatingSerializer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ public void serializeWithType(Object value, JsonGenerator gen, SerializerProvide
174174
// 03-Oct-2012, tatu: This is actually unlikely to work ok... but for now,
175175
// let's give it a chance?
176176
Object delegateValue = convertValue(value);
177+
// consider null (to be consistent with serialize method above)
178+
if (delegateValue == null) {
179+
provider.defaultSerializeNull(gen);
180+
return;
181+
}
177182
JsonSerializer<Object> ser = _delegateSerializer;
178183
if (ser == null) {
179184
ser = _findSerializer(value, provider);

src/test/java/com/fasterxml/jackson/databind/ser/TestCustomSerializers.java

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@
88

99
import org.w3c.dom.Element;
1010

11-
import com.fasterxml.jackson.annotation.JsonFilter;
12-
import com.fasterxml.jackson.annotation.JsonFormat;
13-
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
11+
import com.fasterxml.jackson.annotation.*;
1412

1513
import com.fasterxml.jackson.core.*;
1614
import com.fasterxml.jackson.core.io.CharacterEscapes;
@@ -23,6 +21,8 @@
2321
import com.fasterxml.jackson.databind.ser.std.CollectionSerializer;
2422
import com.fasterxml.jackson.databind.ser.std.StdDelegatingSerializer;
2523
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
24+
import com.fasterxml.jackson.databind.type.TypeFactory;
25+
import com.fasterxml.jackson.databind.util.Converter;
2626
import com.fasterxml.jackson.databind.util.StdConverter;
2727

2828
/**
@@ -187,6 +187,55 @@ public String getId() {
187187
}
188188
}
189189

190+
// [databind#4575]
191+
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
192+
@JsonSubTypes(
193+
{
194+
@JsonSubTypes.Type(Sub4575.class)
195+
}
196+
)
197+
@JsonTypeName("Super")
198+
static class Super4575 {
199+
public static final Super4575 NULL = new Super4575();
200+
}
201+
202+
@JsonTypeName("Sub")
203+
static class Sub4575 extends Super4575 { }
204+
205+
static class NullSerializer4575 extends StdDelegatingSerializer {
206+
public NullSerializer4575(Converter<Object, ?> converter, JavaType delegateType, JsonSerializer<?> delegateSerializer) {
207+
super(converter, delegateType, delegateSerializer);
208+
}
209+
210+
public NullSerializer4575(TypeFactory typeFactory, JsonSerializer<?> delegateSerializer) {
211+
this(
212+
new Converter<Object, Object>() {
213+
@Override
214+
public Object convert(Object value) {
215+
return value == Super4575.NULL ? null : value;
216+
}
217+
218+
@Override
219+
public JavaType getInputType(TypeFactory typeFactory) {
220+
return typeFactory.constructType(delegateSerializer.handledType());
221+
}
222+
223+
@Override
224+
public JavaType getOutputType(TypeFactory typeFactory) {
225+
return typeFactory.constructType(delegateSerializer.handledType());
226+
}
227+
},
228+
typeFactory.constructType(delegateSerializer.handledType() == null ? Object.class : delegateSerializer.handledType()),
229+
delegateSerializer
230+
);
231+
}
232+
233+
@Override
234+
protected StdDelegatingSerializer withDelegate(Converter<Object, ?> converter, JavaType delegateType, JsonSerializer<?> delegateSerializer) {
235+
return new NullSerializer4575(converter, delegateType, delegateSerializer);
236+
}
237+
}
238+
190239
/*
191240
/**********************************************************
192241
/* Unit tests
@@ -208,7 +257,6 @@ public void testCustomization() throws Exception
208257
@SuppressWarnings({ "unchecked", "rawtypes" })
209258
public void testCustomLists() throws Exception
210259
{
211-
ObjectMapper mapper = new ObjectMapper();
212260
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
213261
JsonSerializer<?> ser = new CollectionSerializer(null, false, null, null);
214262
final JsonSerializer<Object> collectionSerializer = (JsonSerializer<Object>) ser;
@@ -225,14 +273,15 @@ public void serialize(Collection value, JsonGenerator gen, SerializerProvider pr
225273
}
226274
}
227275
});
228-
mapper.registerModule(module);
276+
ObjectMapper mapper = jsonMapperBuilder()
277+
.addModule(module)
278+
.build();
229279
assertEquals("null", mapper.writeValueAsString(new ArrayList<Object>()));
230280
}
231281

232282
// [databind#87]: delegating serializer
233283
public void testDelegating() throws Exception
234284
{
235-
ObjectMapper mapper = new ObjectMapper();
236285
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
237286
module.addSerializer(new StdDelegatingSerializer(Immutable.class,
238287
new StdConverter<Immutable, Map<String,Integer>>() {
@@ -245,7 +294,9 @@ public Map<String, Integer> convert(Immutable value)
245294
return map;
246295
}
247296
}));
248-
mapper.registerModule(module);
297+
ObjectMapper mapper = jsonMapperBuilder()
298+
.addModule(module)
299+
.build();
249300
assertEquals("{\"x\":3,\"y\":7}", mapper.writeValueAsString(new Immutable()));
250301
}
251302

@@ -279,8 +330,9 @@ public void testWithCustomElements() throws Exception
279330

280331
SimpleModule module = new SimpleModule("test", Version.unknownVersion());
281332
module.addSerializer(String.class, new UCStringSerializer());
282-
ObjectMapper mapper = new ObjectMapper()
283-
.registerModule(module);
333+
ObjectMapper mapper = jsonMapperBuilder()
334+
.addModule(module)
335+
.build();
284336

285337
assertEquals(q("FOOBAR"), mapper.writeValueAsString("foobar"));
286338
assertEquals(a2q("['FOO',null]"),
@@ -306,4 +358,28 @@ public void testIssue2475() throws Exception {
306358
assertEquals(a2q("{'id':'ID-2','set':[]}"),
307359
writer.writeValueAsString(new Item2475(new HashSet<String>(), "ID-2")));
308360
}
361+
362+
// [databind#4575]
363+
public void testIssue4575() throws Exception {
364+
com.fasterxml.jackson.databind.Module module = new SimpleModule() {
365+
{
366+
setSerializerModifier(new BeanSerializerModifier() {
367+
@Override
368+
public JsonSerializer<?> modifySerializer(
369+
SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer
370+
) {
371+
return new NullSerializer4575(config.getTypeFactory(), serializer);
372+
}
373+
});
374+
}
375+
};
376+
377+
ObjectMapper mapper = jsonMapperBuilder()
378+
.addModule(module)
379+
.build();
380+
381+
assertEquals("{\"@type\":\"Super\"}", mapper.writeValueAsString(new Super4575()));
382+
assertEquals("{\"@type\":\"Sub\"}", mapper.writeValueAsString(new Sub4575()));
383+
assertEquals("null", mapper.writeValueAsString(Super4575.NULL));
384+
}
309385
}

0 commit comments

Comments
 (0)