Skip to content

Commit ecc4005

Browse files
committed
Avoid affecting serialization of custom Page implementations in legacy mode.
Registering a StdConverter with Jackson to log a warning about the Page serialization mode causes the target serializer to be only built for Page losing additional properties defined on extensions. We now instead register a no-op BeanSerializerModifier that issues the warning and doesn't affect the serializer selection. Fixes GH-3137.
1 parent 2cc8d04 commit ecc4005

File tree

3 files changed

+45
-58
lines changed

3 files changed

+45
-58
lines changed

src/main/java/org/springframework/data/web/config/SpringDataJacksonConfiguration.java

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.web.config;
1717

18+
import java.util.List;
19+
1820
import org.slf4j.Logger;
1921
import org.slf4j.LoggerFactory;
2022
import org.springframework.beans.factory.annotation.Autowired;
@@ -27,8 +29,12 @@
2729
import org.springframework.lang.Nullable;
2830
import org.springframework.util.ClassUtils;
2931

32+
import com.fasterxml.jackson.databind.BeanDescription;
33+
import com.fasterxml.jackson.databind.SerializationConfig;
3034
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
3135
import com.fasterxml.jackson.databind.module.SimpleModule;
36+
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
37+
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
3238
import com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase;
3339
import com.fasterxml.jackson.databind.util.StdConverter;
3440

@@ -83,7 +89,8 @@ public PageModule(@Nullable SpringDataWebSettings settings) {
8389
addSerializer(UNPAGED_TYPE, new UnpagedAsInstanceSerializer());
8490

8591
if (settings == null || settings.pageSerializationMode() == PageSerializationMode.DIRECT) {
86-
setMixInAnnotation(PageImpl.class, WarningMixing.class);
92+
setSerializerModifier(new WarningLoggingModifier());
93+
8794
} else {
8895
setMixInAnnotation(PageImpl.class, WrappingMixing.class);
8996
}
@@ -109,14 +116,6 @@ public String valueToString(@Nullable Object value) {
109116
}
110117
}
111118

112-
/**
113-
* A mixin for PageImpl to register a converter issuing the serialization warning.
114-
*
115-
* @author Oliver Drotbohm
116-
*/
117-
@JsonSerialize(converter = PlainPageSerializationWarning.class)
118-
abstract class WarningMixing {}
119-
120119
@JsonSerialize(converter = PageModelConverter.class)
121120
abstract class WrappingMixing {}
122121

@@ -129,27 +128,35 @@ public PagedModel<?> convert(@Nullable Page<?> value) {
129128
}
130129
}
131130

132-
static class PlainPageSerializationWarning extends StdConverter<Page<?>, Page<?>> {
131+
/**
132+
* A {@link BeanSerializerModifier} that logs a warning message if an instance of {@link Page} will be rendered.
133+
*
134+
* @author Oliver Drotbohm
135+
*/
136+
static class WarningLoggingModifier extends BeanSerializerModifier {
133137

134-
private static final Logger LOGGER = LoggerFactory.getLogger(PlainPageSerializationWarning.class);
138+
private static final Logger LOGGER = LoggerFactory.getLogger(WarningLoggingModifier.class);
135139
private static final String MESSAGE = """
136140
Serializing PageImpl instances as-is is not supported, meaning that there is no guarantee about the stability of the resulting JSON structure!
137141
For a stable JSON structure, please use Spring Data's PagedModel (globally via @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO))
138142
or Spring HATEOAS and Spring Data's PagedResourcesAssembler as documented in https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html#core.web.pageables.
139143
""";
140144

145+
private static final long serialVersionUID = 954857444010009875L;
146+
141147
private boolean warningRendered = false;
142148

143-
@Nullable
144149
@Override
145-
public Page<?> convert(@Nullable Page<?> value) {
150+
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
151+
List<BeanPropertyWriter> beanProperties) {
152+
153+
if (Page.class.isAssignableFrom(beanDesc.getBeanClass()) && !warningRendered) {
146154

147-
if (!warningRendered) {
148155
this.warningRendered = true;
149156
LOGGER.warn(MESSAGE);
150157
}
151158

152-
return value;
159+
return super.changeProperties(config, beanDesc, beanProperties);
153160
}
154161
}
155162
}

src/test/java/org/springframework/data/web/PageImplJsonSerializationUnitTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,16 @@ void serializesPageImplAsPagedModel() {
4545
assertJsonRendering(PageSerializationMode.VIA_DTO, "$.content", "$.page");
4646
}
4747

48+
@Test // GH-3137
49+
void serializesCustomPageAsPageImpl() {
50+
assertJsonRendering(PageSerializationMode.DIRECT, new Extension<>("header"), "$.pageable", "$.last", "$.first");
51+
}
52+
4853
private static void assertJsonRendering(PageSerializationMode mode, String... jsonPaths) {
54+
assertJsonRendering(mode, new PageImpl<>(Collections.emptyList()), jsonPaths);
55+
}
56+
57+
private static void assertJsonRendering(PageSerializationMode mode, PageImpl<?> page, String... jsonPaths) {
4958

5059
SpringDataWebSettings settings = new SpringDataWebSettings(mode);
5160

@@ -54,11 +63,24 @@ private static void assertJsonRendering(PageSerializationMode mode, String... js
5463

5564
assertThatNoException().isThrownBy(() -> {
5665

57-
String result = mapper.writeValueAsString(new PageImpl<>(Collections.emptyList()));
66+
String result = mapper.writeValueAsString(page);
5867

5968
for (String jsonPath : jsonPaths) {
6069
assertThat(JsonPath.<Object> read(result, jsonPath)).isNotNull();
6170
}
6271
});
6372
}
73+
74+
static class Extension<T> extends PageImpl<T> {
75+
76+
private Object header;
77+
78+
public Extension(Object header) {
79+
super(Collections.emptyList());
80+
}
81+
82+
public Object getHeader() {
83+
return header;
84+
}
85+
}
6486
}

src/test/java/org/springframework/data/web/config/SpringDataJacksonConfigurationUnitTests.java

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)