Skip to content

Commit 523a231

Browse files
mp911deodrotbohm
authored andcommitted
GH-2483 - Switch to Jackson 3.
1 parent bfb2a5b commit 523a231

File tree

41 files changed

+3134
-207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+3134
-207
lines changed

spring-data-rest-core/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,6 @@
5757
<artifactId>jackson-annotations</artifactId>
5858
</dependency>
5959

60-
<dependency>
61-
<groupId>com.fasterxml.jackson.datatype</groupId>
62-
<artifactId>jackson-datatype-jdk8</artifactId>
63-
</dependency>
64-
6560
<dependency>
6661
<groupId>org.jmolecules</groupId>
6762
<artifactId>jmolecules-ddd</artifactId>

spring-data-rest-tests/spring-data-rest-tests-jpa/src/test/java/org/springframework/data/rest/webmvc/json/RepositoryTestsConfig.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717

1818
import static org.mockito.Mockito.*;
1919

20+
import tools.jackson.databind.DeserializationFeature;
21+
import tools.jackson.databind.JacksonModule;
22+
import tools.jackson.databind.ObjectMapper;
23+
import tools.jackson.databind.json.JsonMapper;
24+
2025
import java.util.Collections;
2126
import java.util.List;
2227

@@ -41,7 +46,7 @@
4146
import org.springframework.data.rest.webmvc.EmbeddedResourcesAssembler;
4247
import org.springframework.data.rest.webmvc.jpa.Person;
4348
import org.springframework.data.rest.webmvc.jpa.PersonRepository;
44-
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.LookupObjectSerializer;
49+
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson3Module.LookupObjectSerializer;
4550
import org.springframework.data.rest.webmvc.mapping.Associations;
4651
import org.springframework.data.rest.webmvc.mapping.DefaultLinkCollector;
4752
import org.springframework.data.rest.webmvc.mapping.LinkCollector;
@@ -51,9 +56,6 @@
5156
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
5257
import org.springframework.format.support.DefaultFormattingConversionService;
5358
import org.springframework.format.support.FormattingConversionService;
54-
import org.springframework.hateoas.mediatype.MessageResolver;
55-
import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider;
56-
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
5759
import org.springframework.hateoas.server.EntityLinks;
5860
import org.springframework.hateoas.server.LinkRelationProvider;
5961
import org.springframework.hateoas.server.RepresentationModelProcessor;
@@ -62,9 +64,6 @@
6264
import org.springframework.plugin.core.PluginRegistry;
6365

6466
import com.fasterxml.jackson.annotation.JsonInclude.Include;
65-
import com.fasterxml.jackson.databind.DeserializationFeature;
66-
import com.fasterxml.jackson.databind.Module;
67-
import com.fasterxml.jackson.databind.ObjectMapper;
6867

6968
/**
7069
* @author Jon Brisbin
@@ -114,7 +113,7 @@ public PersistentEntities persistentEntities() {
114113
}
115114

116115
@Bean
117-
public Module persistentEntityModule() {
116+
public JacksonModule persistentEntityModule() {
118117

119118
var conversionService = new DefaultConversionService();
120119

@@ -132,7 +131,7 @@ public Module persistentEntityModule() {
132131
Associations associations = new Associations(mappings, config());
133132
LinkCollector collector = new DefaultLinkCollector(persistentEntities(), selfLinkProvider, associations);
134133

135-
return new PersistentEntityJackson2Module(associations, persistentEntities(), uriToEntityConverter, collector,
134+
return new PersistentEntityJackson3Module(associations, persistentEntities(), uriToEntityConverter, collector,
136135
invokerFactory, mock(LookupObjectSerializer.class),
137136
new RepresentationModelProcessorInvoker(Collections.<RepresentationModelProcessor<?>> emptyList()),
138137
new EmbeddedResourcesAssembler(persistentEntities(), associations, mock(ExcerptProjector.class)));
@@ -142,15 +141,18 @@ invokerFactory, mock(LookupObjectSerializer.class),
142141
public ObjectMapper objectMapper() {
143142

144143
LinkRelationProvider relProvider = new EvoInflectorLinkRelationProvider();
145-
ObjectMapper mapper = new ObjectMapper();
144+
JsonMapper.Builder builder = JsonMapper.builder();
145+
146+
// TODO: Jackson 3 HAL module
147+
// builder.addModule(new Jackson2HalModule());
148+
builder.addModule(persistentEntityModule());
149+
150+
/*builder.handlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
151+
new DefaultCurieProvider(Collections.emptyMap()), MessageResolver.DEFAULTS_ONLY)); */
146152

147-
mapper.registerModule(new Jackson2HalModule());
148-
mapper.registerModule(persistentEntityModule());
149-
mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider,
150-
new DefaultCurieProvider(Collections.emptyMap()), MessageResolver.DEFAULTS_ONLY));
151-
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
152-
mapper.setSerializationInclusion(Include.NON_EMPTY);
153+
builder.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
154+
builder.changeDefaultPropertyInclusion(it -> it.withValueInclusion(Include.NON_NULL));
153155

154-
return mapper;
156+
return builder.build();
155157
}
156158
}

spring-data-rest-webmvc/pom.xml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
<!-- Jackson -->
5353

5454
<dependency>
55-
<groupId>com.fasterxml.jackson.core</groupId>
55+
<groupId>tools.jackson.core</groupId>
5656
<artifactId>jackson-databind</artifactId>
5757
</dependency>
5858

@@ -61,12 +61,25 @@
6161
<artifactId>jackson-annotations</artifactId>
6262
</dependency>
6363

64+
<dependency>
65+
<groupId>com.fasterxml.jackson.core</groupId>
66+
<artifactId>jackson-databind</artifactId>
67+
<optional>true</optional>
68+
</dependency>
69+
6470
<!-- Jackson Hibernate -->
6571

6672
<dependency>
6773
<groupId>com.fasterxml.jackson.datatype</groupId>
6874
<artifactId>jackson-datatype-hibernate6</artifactId>
6975
<optional>true</optional>
76+
77+
<exclusions>
78+
<exclusion>
79+
<groupId>com.fasterxml.jackson.core</groupId>
80+
<artifactId>*</artifactId>
81+
</exclusion>
82+
</exclusions>
7083
</dependency>
7184

7285
<dependency>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.rest.webmvc.alps;
17+
18+
import tools.jackson.databind.ObjectMapper;
19+
import tools.jackson.databind.SerializationFeature;
20+
import tools.jackson.databind.cfg.MapperBuilder;
21+
import tools.jackson.databind.json.JsonMapper;
22+
23+
import java.util.Arrays;
24+
import java.util.Collections;
25+
26+
import org.jspecify.annotations.Nullable;
27+
28+
import org.springframework.core.MethodParameter;
29+
import org.springframework.core.ResolvableType;
30+
import org.springframework.core.convert.converter.Converter;
31+
import org.springframework.data.rest.webmvc.RootResourceInformation;
32+
import org.springframework.hateoas.MediaTypes;
33+
import org.springframework.hateoas.mediatype.alps.Alps;
34+
import org.springframework.http.MediaType;
35+
import org.springframework.http.converter.HttpMessageConverter;
36+
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
37+
import org.springframework.http.server.ServerHttpRequest;
38+
import org.springframework.http.server.ServerHttpResponse;
39+
import org.springframework.util.Assert;
40+
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
41+
42+
import com.fasterxml.jackson.annotation.JsonInclude.Include;
43+
44+
/**
45+
* {@link HttpMessageConverter} to render {@link Alps} and {@link RootResourceInformation} instances as
46+
* {@code application/alps+json}.
47+
*
48+
* @author Mark Paluch
49+
* @author Oliver Gierke
50+
* @author Greg Turnquist
51+
* @since 5.0
52+
*/
53+
public class AlpsJackson3JsonHttpMessageConverter extends JacksonJsonHttpMessageConverter
54+
implements ResponseBodyAdvice<Object> {
55+
56+
private final RootResourceInformationToAlpsDescriptorConverter converter;
57+
58+
/**
59+
* Creates a new {@link AlpsJackson3JsonHttpMessageConverter} for the given {@link Converter}.
60+
*
61+
* @param converter must not be {@literal null}.
62+
*/
63+
public AlpsJackson3JsonHttpMessageConverter(RootResourceInformationToAlpsDescriptorConverter converter) {
64+
this(JsonMapper.builder(), converter);
65+
}
66+
67+
/**
68+
* Creates a new {@link AlpsJackson3JsonHttpMessageConverter} for the given {@link Converter}.
69+
*
70+
* @param converter must not be {@literal null}.
71+
*/
72+
public AlpsJackson3JsonHttpMessageConverter(ObjectMapper objectMapper,
73+
RootResourceInformationToAlpsDescriptorConverter converter) {
74+
this(objectMapper.rebuild(), converter);
75+
}
76+
77+
/**
78+
* Creates a new {@link AlpsJackson3JsonHttpMessageConverter} for the given {@link Converter}.
79+
*
80+
* @param converter must not be {@literal null}.
81+
*/
82+
AlpsJackson3JsonHttpMessageConverter(
83+
MapperBuilder<? extends ObjectMapper, ? extends MapperBuilder<?, ?>> objectMapper,
84+
RootResourceInformationToAlpsDescriptorConverter converter) {
85+
86+
super(objectMapper.changeDefaultPropertyInclusion(it -> it.withValueInclusion(Include.NON_EMPTY))
87+
.enable(SerializationFeature.INDENT_OUTPUT).build());
88+
Assert.notNull(converter, "Converter must not be null");
89+
90+
this.converter = converter;
91+
92+
setSupportedMediaTypes(Arrays.asList(MediaTypes.ALPS_JSON, MediaType.APPLICATION_JSON, MediaType.ALL));
93+
}
94+
95+
@Override
96+
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
97+
return (clazz.isAssignableFrom(Alps.class) || clazz.isAssignableFrom(RootResourceInformation.class))
98+
&& super.canWrite(clazz, mediaType);
99+
}
100+
101+
@Override
102+
public boolean canRead(ResolvableType type, @Nullable MediaType mediaType) {
103+
return false;
104+
}
105+
106+
@Override
107+
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
108+
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
109+
ServerHttpResponse response) {
110+
111+
return body instanceof RootResourceInformation
112+
? Collections.singletonMap("alps", converter.convert((RootResourceInformation) body))
113+
: body;
114+
}
115+
116+
@Override
117+
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
118+
return converterType.equals(AlpsJackson3JsonHttpMessageConverter.class);
119+
}
120+
}

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/AlpsJsonHttpMessageConverter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
*
4444
* @author Oliver Gierke
4545
* @author Greg Turnquist
46+
* @deprecated since 5.0, in favor of {@link AlpsJackson3JsonHttpMessageConverter}.
4647
*/
48+
@Deprecated(since = "5.0", forRemoval = true)
4749
public class AlpsJsonHttpMessageConverter extends MappingJackson2HttpMessageConverter
4850
implements ResponseBodyAdvice<Object> {
4951

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/alps/RootResourceInformationToAlpsDescriptorConverter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717

1818
import static org.springframework.hateoas.mediatype.alps.Alps.*;
1919

20+
import tools.jackson.databind.ObjectMapper;
21+
import tools.jackson.databind.introspect.AnnotatedMethod;
22+
import tools.jackson.databind.introspect.BeanPropertyDefinition;
23+
2024
import java.util.ArrayList;
2125
import java.util.Arrays;
2226
import java.util.Collection;
@@ -66,10 +70,6 @@
6670
import org.springframework.util.Assert;
6771
import org.springframework.util.StringUtils;
6872

69-
import com.fasterxml.jackson.databind.ObjectMapper;
70-
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
71-
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
72-
7373
/**
7474
* Converter to create Alps {@link Descriptor} instances for a {@link RootResourceInformation}.
7575
*

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/aot/ValueInstantiatorCustomizerRuntimeHints.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,35 @@
1919

2020
import org.springframework.aot.hint.RuntimeHints;
2121
import org.springframework.aot.hint.RuntimeHintsRegistrar;
22-
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module.AssociationUriResolvingDeserializerModifier.ValueInstantiatorCustomizer;
22+
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module;
23+
import org.springframework.data.rest.webmvc.json.PersistentEntityJackson3Module;
24+
import org.springframework.util.ClassUtils;
2325

2426
import com.fasterxml.jackson.databind.deser.std.StdValueInstantiator;
2527

2628
/**
2729
* Registers the {@link StdValueInstantiator}'s {@code _constructorArgs} field for reflection as needed by
28-
* {@link ValueInstantiatorCustomizer}.
30+
* {@code ValueInstantiatorCustomizer}.
2931
*
3032
* @author Oliver Drotbohm
33+
* @author Mark Paluch
3134
* @since 4.0.2
3235
* @soundtrack The Intersphere - Wanderer (https://www.youtube.com/watch?v=Sp_VyFBbDPA)
3336
*/
37+
@SuppressWarnings("removal")
3438
class ValueInstantiatorCustomizerRuntimeHints implements RuntimeHintsRegistrar {
3539

3640
@Override
3741
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
38-
hints.reflection().registerField(ValueInstantiatorCustomizer.CONSTRUCTOR_ARGS_FIELD);
42+
43+
if (ClassUtils.isPresent("tools.jackson.databind.ObjectMapper", classLoader)) {
44+
hints.reflection().registerField(
45+
PersistentEntityJackson3Module.AssociationUriResolvingDeserializerModifier.ValueInstantiatorCustomizer.CONSTRUCTOR_ARGS_FIELD);
46+
}
47+
48+
if (ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)) {
49+
hints.reflection().registerField(
50+
PersistentEntityJackson2Module.AssociationUriResolvingDeserializerModifier.ValueInstantiatorCustomizer.CONSTRUCTOR_ARGS_FIELD);
51+
}
3952
}
4053
}

spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/config/JsonPatchHandler.java

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

18+
import tools.jackson.databind.ObjectMapper;
19+
import tools.jackson.databind.node.ObjectNode;
20+
1821
import java.io.InputStream;
1922

2023
import org.springframework.data.rest.webmvc.IncomingRequest;
@@ -28,9 +31,6 @@
2831
import org.springframework.http.converter.HttpMessageNotReadableException;
2932
import org.springframework.util.Assert;
3033

31-
import com.fasterxml.jackson.databind.ObjectMapper;
32-
import com.fasterxml.jackson.databind.node.ObjectNode;
33-
3434
/**
3535
* Component to apply JSON Patch and JSON Merge Patch payloads to existing domain objects. The implementation uses the
3636
* JSON Patch library by Francis Galiegue but applies a customization to remove operations. As the patched node set

0 commit comments

Comments
 (0)