Skip to content

Commit aa6f0ac

Browse files
mariofuscogsmet
authored andcommitted
Avoid using generated Jackson serializers for subclasses
(cherry picked from commit 3a4b8b1)
1 parent 3862033 commit aa6f0ac

File tree

8 files changed

+121
-6
lines changed

8 files changed

+121
-6
lines changed

extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonCodeGenerator.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.jboss.jandex.Type;
2626
import org.jboss.jandex.TypeVariable;
2727

28+
import com.fasterxml.jackson.annotation.JsonIgnore;
2829
import com.fasterxml.jackson.annotation.JsonProperty;
2930

3031
import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
@@ -337,9 +338,13 @@ boolean hasUnknownAnnotation() {
337338
return annotations.keySet().stream().anyMatch(FieldSpecs::isUnknownAnnotation);
338339
}
339340

341+
boolean isIgnoredField() {
342+
return annotations.get(JsonIgnore.class.getName()) != null;
343+
}
344+
340345
private static boolean isUnknownAnnotation(String ann) {
341346
if (ann.startsWith("com.fasterxml.jackson.")) {
342-
return !ann.equals(JsonProperty.class.getName());
347+
return !ann.equals(JsonProperty.class.getName()) && !ann.equals(JsonIgnore.class.getName());
343348
}
344349
return false;
345350
}

extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonDeserializerFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,9 @@ private boolean deserializeFieldSpecs(DeserializationData deserData, ResultHandl
342342
ResultHandle objHandle, ResultHandle fieldValue, Set<String> deserializedFields, Switch.StringSwitch strSwitch,
343343
FieldSpecs fieldSpecs, AtomicBoolean valid) {
344344
if (fieldSpecs != null && deserializedFields.add(fieldSpecs.jsonName)) {
345+
if (fieldSpecs.isIgnoredField()) {
346+
return true;
347+
}
345348
if (fieldSpecs.hasUnknownAnnotation()) {
346349
return false;
347350
}

extensions/resteasy-reactive/rest-jackson/deployment/src/main/java/io/quarkus/resteasy/reactive/jackson/deployment/processor/JacksonSerializerFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ private boolean serializeFields(ClassInfo classInfo, ClassCreator classCreator,
223223
for (FieldInfo fieldInfo : classFields(classInfo)) {
224224
FieldSpecs fieldSpecs = fieldSpecsFromField(classInfo, fieldInfo);
225225
if (fieldSpecs != null && serializedFields.add(fieldSpecs.jsonName)) {
226+
if (fieldSpecs.isIgnoredField()) {
227+
continue;
228+
}
226229
if (fieldSpecs.hasUnknownAnnotation()) {
227230
return false;
228231
}
@@ -237,6 +240,9 @@ private boolean serializeMethods(ClassInfo classInfo, ClassCreator classCreator,
237240
for (MethodInfo methodInfo : classMethods(classInfo)) {
238241
FieldSpecs fieldSpecs = fieldSpecsFromMethod(methodInfo);
239242
if (fieldSpecs != null && serializedFields.add(fieldSpecs.jsonName)) {
243+
if (fieldSpecs.isIgnoredField()) {
244+
continue;
245+
}
240246
if (fieldSpecs.hasUnknownAnnotation()) {
241247
return false;
242248
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.quarkus.resteasy.reactive.jackson.deployment.test;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnore;
4+
5+
public class ItemExtended extends Item {
6+
7+
private String nameExtended;
8+
9+
@JsonIgnore
10+
private String emailExtended;
11+
12+
public String getNameExtended() {
13+
return nameExtended;
14+
}
15+
16+
public void setNameExtended(String nameExtended) {
17+
this.nameExtended = nameExtended;
18+
}
19+
20+
public String getEmailExtended() {
21+
return emailExtended;
22+
}
23+
24+
public void setEmailExtended(String emailExtended) {
25+
this.emailExtended = emailExtended;
26+
}
27+
}

extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonResource.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,26 @@ public ContainerDTO interfaceTest() {
467467
return new ContainerDTO(NestedInterface.INSTANCE);
468468
}
469469

470+
@GET
471+
@Path("/item")
472+
public Item getItem() {
473+
Item item = new Item();
474+
item.setName("Name");
475+
item.setEmail("E-mail");
476+
return item;
477+
}
478+
479+
@GET
480+
@Path("/item-extended")
481+
public ItemExtended getItemExtended() {
482+
ItemExtended item = new ItemExtended();
483+
item.setName("Name");
484+
item.setEmail("E-mail");
485+
item.setNameExtended("Name-Extended");
486+
item.setEmailExtended("E-mail-Extended");
487+
return item;
488+
}
489+
470490
public static class UnquotedFieldsPersonSerialization implements BiFunction<ObjectMapper, Type, ObjectWriter> {
471491

472492
public static final AtomicInteger count = new AtomicInteger();

extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonTest.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.quarkus.resteasy.reactive.jackson.deployment.test;
22

33
import static org.hamcrest.Matchers.containsString;
4+
import static org.hamcrest.Matchers.emptyOrNullString;
45
import static org.hamcrest.Matchers.is;
56
import static org.hamcrest.Matchers.not;
67
import static org.hamcrest.Matchers.notNullValue;
@@ -37,7 +38,7 @@ public JavaArchive get() {
3738
AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class,
3839
Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class,
3940
NestedInterface.class, StateRecord.class, MapWrapper.class, GenericWrapper.class,
40-
Fruit.class, Price.class, DogRecord.class)
41+
Fruit.class, Price.class, DogRecord.class, ItemExtended.class)
4142
.addAsResource(new StringAsset("admin-expression=admin\n" +
4243
"user-expression=user\n" +
4344
"birth-date-roles=alice,bob\n"), "application.properties");
@@ -782,4 +783,28 @@ public void testNullMapEcho() {
782783
.body("name", Matchers.is("test"))
783784
.body("properties", Matchers.nullValue());
784785
}
786+
787+
@Test
788+
public void testItem() {
789+
RestAssured
790+
.with()
791+
.get("/simple/item")
792+
.then()
793+
.statusCode(200)
794+
.body("name", Matchers.is("Name"))
795+
.body("email", Matchers.is("E-mail"));
796+
}
797+
798+
@Test
799+
public void testItemExtended() {
800+
RestAssured
801+
.with()
802+
.get("/simple/item-extended")
803+
.then()
804+
.statusCode(200)
805+
.body("name", Matchers.is("Name"))
806+
.body("email", Matchers.is("E-mail"))
807+
.body("nameExtended", Matchers.is("Name-Extended"))
808+
.body("emailExtended", Matchers.is(emptyOrNullString()));
809+
}
785810
}

extensions/resteasy-reactive/rest-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/SimpleJsonWithReflectionFreeSerializersTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public JavaArchive get() {
2828
AbstractUnsecuredPet.class, UnsecuredPet.class, SecuredPersonInterface.class, Frog.class,
2929
Pond.class, FrogBodyParts.class, FrogBodyParts.BodyPart.class, ContainerDTO.class,
3030
NestedInterface.class, StateRecord.class, MapWrapper.class, GenericWrapper.class,
31-
Fruit.class, Price.class, DogRecord.class)
31+
Fruit.class, Price.class, DogRecord.class, ItemExtended.class)
3232
.addAsResource(new StringAsset("admin-expression=admin\n" +
3333
"user-expression=user\n" +
3434
"birth-date-roles=alice,bob\n" +

extensions/resteasy-reactive/rest-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/GeneratedSerializersRegister.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
package io.quarkus.resteasy.reactive.jackson.runtime.serialisers;
22

33
import java.lang.reflect.InvocationTargetException;
4+
import java.util.HashMap;
5+
import java.util.Map;
46

57
import jakarta.inject.Singleton;
68

9+
import com.fasterxml.jackson.databind.BeanDescription;
10+
import com.fasterxml.jackson.databind.JavaType;
11+
import com.fasterxml.jackson.databind.JsonSerializer;
12+
import com.fasterxml.jackson.databind.Module;
713
import com.fasterxml.jackson.databind.ObjectMapper;
14+
import com.fasterxml.jackson.databind.SerializationConfig;
815
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
916
import com.fasterxml.jackson.databind.module.SimpleModule;
17+
import com.fasterxml.jackson.databind.module.SimpleSerializers;
1018
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
1119

1220
import io.quarkus.jackson.ObjectMapperCustomizer;
@@ -21,21 +29,26 @@ public void customize(ObjectMapper objectMapper) {
2129
}
2230

2331
static class MappingModuleHolder {
24-
static final SimpleModule mappingModule = createMappingModule();
32+
static final Module mappingModule = createMappingModule();
2533

26-
private static SimpleModule createMappingModule() {
34+
private static Module createMappingModule() {
2735
SimpleModule module = new SimpleModule();
36+
// Use a custom SimpleSerializers to use a json serializer only if it has been generated for that
37+
// exact class and not one of its sublclasses. This is already the default behaviour for deserializers.
38+
ExactSerializers serializers = new ExactSerializers();
2839

2940
for (Class<? extends StdSerializer> serClass : ResteasyReactiveServerJacksonRecorder.getGeneratedSerializers()) {
3041
try {
3142
StdSerializer serializer = serClass.getConstructor().newInstance();
32-
module.addSerializer(serializer.handledType(), serializer);
43+
serializers.addExactSerializer(serializer.handledType(), serializer);
3344
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
3445
| NoSuchMethodException e) {
3546
throw new RuntimeException(e);
3647
}
3748
}
3849

50+
module.setSerializers(serializers);
51+
3952
for (Class<? extends StdDeserializer> deserClass : ResteasyReactiveServerJacksonRecorder
4053
.getGeneratedDeserializers()) {
4154
try {
@@ -49,5 +62,21 @@ private static SimpleModule createMappingModule() {
4962

5063
return module;
5164
}
65+
66+
}
67+
68+
public static class ExactSerializers extends SimpleSerializers {
69+
70+
private final Map<Class<?>, JsonSerializer<?>> exactSerializers = new HashMap<>();
71+
72+
public <T> void addExactSerializer(Class<? extends T> type, JsonSerializer<T> ser) {
73+
exactSerializers.put(type, ser);
74+
}
75+
76+
@Override
77+
public JsonSerializer<?> findSerializer(SerializationConfig config, JavaType type, BeanDescription beanDesc) {
78+
JsonSerializer<?> exactSerializer = exactSerializers.get(type.getRawClass());
79+
return exactSerializer != null ? exactSerializer : super.findSerializer(config, type, beanDesc);
80+
}
5281
}
5382
}

0 commit comments

Comments
 (0)