diff --git a/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java b/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java
index 9ee20abd90c..c0f6a06d644 100644
--- a/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java
+++ b/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java
@@ -165,6 +165,7 @@ public class AntoraAsciidocAttributes {
addDependencyVersion(attributes, "jackson-core", "tools.jackson.core:jackson-core");
addDependencyVersion(attributes, "jackson-databind", "tools.jackson.core:jackson-databind");
addDependencyVersion(attributes, "jackson-dataformat-xml", "tools.jackson.dataformat:jackson-dataformat-xml");
+ addDependencyVersion(attributes, "jackson2-databind", "com.fasterxml.jackson.core:jackson-databind");
addSpringDataDependencyVersion(attributes, internal, "spring-data-commons");
addSpringDataDependencyVersion(attributes, internal, "spring-data-couchbase");
addSpringDataDependencyVersion(attributes, internal, "spring-data-cassandra");
diff --git a/buildSrc/src/main/resources/org/springframework/boot/build/antora/antora-asciidoc-attributes.properties b/buildSrc/src/main/resources/org/springframework/boot/build/antora/antora-asciidoc-attributes.properties
index e025675d02f..cbe5e560a02 100644
--- a/buildSrc/src/main/resources/org/springframework/boot/build/antora/antora-asciidoc-attributes.properties
+++ b/buildSrc/src/main/resources/org/springframework/boot/build/antora/antora-asciidoc-attributes.properties
@@ -80,10 +80,12 @@ url-jackson-annotations-javadoc=https://javadoc.io/doc/com.fasterxml.jackson.cor
url-jackson-core-javadoc=https://javadoc.io/doc/tools.jackson.core/jackson-core/{version-jackson-core}
url-jackson-databind-javadoc=https://javadoc.io/doc/tools.jackson.core/jackson-databind/{version-jackson-databind}
url-jackson-dataformat-xml-javadoc=https://javadoc.io/doc/tools.jackson.dataformat/jackson-dataformat-xml/{version-jackson-dataformat-xml}
+url-jackson2-databind-javadoc=https://javadoc.io/doc/com.fasterxml.jackson.core/jackson-databind/{version-jackson2-databind}
# === Javadoc Locations ===
javadoc-location-com-fasterxml-jackson-annotation={url-jackson-annotations-javadoc}
+javadoc-location-com-fasterxml-jackson-databind={url-jackson2-databind-javadoc}
javadoc-location-org-apache-pulsar-client-api={url-pulsar-client-api-javadoc}
javadoc-location-org-apache-pulsar-reactive-client-api={url-pulsar-client-reactive-api-javadoc}
javadoc-location-org-springframework-data-cassandra={url-spring-data-cassandra-javadoc}
diff --git a/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java b/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java
index ea22801d174..769bf5d1a2b 100644
--- a/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java
+++ b/buildSrc/src/test/java/org/springframework/boot/build/antora/AntoraAsciidocAttributesTests.java
@@ -282,8 +282,10 @@ class AntoraAsciidocAttributesTests {
addMockTestcontainersVersion(versions, "redpanda", version);
addMockTestcontainersVersion(versions, "r2dbc", version);
addMockJackson2CoreVersion(versions, "jackson-annotations", version);
+ addMockJackson2CoreVersion(versions, "jackson-databind", version);
addMockJacksonCoreVersion(versions, "jackson-core", version);
addMockJacksonCoreVersion(versions, "jackson-databind", version);
+ addMockJacksonCoreVersion(versions, "jackson-databind", version);
versions.put("org.apache.pulsar:pulsar-client-api", version);
versions.put("org.apache.pulsar:pulsar-client-reactive-api", version);
versions.put("tools.jackson.dataformat:jackson-dataformat-xml", version);
diff --git a/core/spring-boot-test/build.gradle b/core/spring-boot-test/build.gradle
index 156829f7063..8720ea9e605 100644
--- a/core/spring-boot-test/build.gradle
+++ b/core/spring-boot-test/build.gradle
@@ -27,7 +27,7 @@ dependencies {
api(project(":core:spring-boot"))
api("org.springframework:spring-test")
- optional("tools.jackson.core:jackson-databind")
+ optional("com.fasterxml.jackson.core:jackson-databind")
optional("com.google.code.gson:gson")
optional("com.jayway.jsonpath:json-path")
optional("jakarta.json.bind:jakarta.json.bind-api")
@@ -45,6 +45,7 @@ dependencies {
optional("org.seleniumhq.selenium:selenium-api")
optional("org.skyscreamer:jsonassert")
optional("org.springframework:spring-web")
+ optional("tools.jackson.core:jackson-databind")
testImplementation(project(":test-support:spring-boot-test-support"))
testImplementation("ch.qos.logback:logback-classic")
diff --git a/core/spring-boot-test/src/main/java/org/springframework/boot/test/json/Jackson2Tester.java b/core/spring-boot-test/src/main/java/org/springframework/boot/test/json/Jackson2Tester.java
new file mode 100644
index 00000000000..99cf33f8072
--- /dev/null
+++ b/core/spring-boot-test/src/main/java/org/springframework/boot/test/json/Jackson2Tester.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2012-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.boot.test.json;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectReader;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.jayway.jsonpath.Configuration;
+import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
+import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
+import org.jspecify.annotations.Nullable;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.core.ResolvableType;
+import org.springframework.util.Assert;
+
+/**
+ * AssertJ based JSON tester backed by Jackson 2. Usually instantiated via
+ * {@link #initFields(Object, ObjectMapper)}, for example:
+ * public class ExampleObjectJsonTests {
+ *
+ * private Jackson2Tester<ExampleObject> json;
+ *
+ * @Before
+ * public void setup() {
+ * ObjectMapper objectMapper = new ObjectMapper();
+ * Jackson2Tester.initFields(this, objectMapper);
+ * }
+ *
+ * @Test
+ * public void testWriteJson() throws IOException {
+ * ExampleObject object = //...
+ * assertThat(json.write(object)).isEqualToJson("expected.json");
+ * }
+ *
+ * }
+ *
+ *
+ * See {@link AbstractJsonMarshalTester} for more details.
+ *
+ * @param the type under test
+ * @author Phillip Webb
+ * @author Madhura Bhave
+ * @author Diego Berrueta
+ * @since 4.0.0
+ * @deprecated since 4.0.0 for removal in 4.2.0 in favor of Jackson 3.
+ */
+@Deprecated(since = "4.0.0", forRemoval = true)
+public class Jackson2Tester extends AbstractJsonMarshalTester {
+
+ private final ObjectMapper objectMapper;
+
+ private @Nullable Class> view;
+
+ /**
+ * Create a new {@link Jackson2Tester} instance.
+ * @param objectMapper the Jackson object mapper
+ */
+ protected Jackson2Tester(ObjectMapper objectMapper) {
+ Assert.notNull(objectMapper, "'objectMapper' must not be null");
+ this.objectMapper = objectMapper;
+ }
+
+ /**
+ * Create a new {@link Jackson2Tester} instance.
+ * @param resourceLoadClass the source class used to load resources
+ * @param type the type under test
+ * @param objectMapper the Jackson object mapper
+ */
+ public Jackson2Tester(Class> resourceLoadClass, ResolvableType type, ObjectMapper objectMapper) {
+ this(resourceLoadClass, type, objectMapper, null);
+ }
+
+ /**
+ * Create a new {@link Jackson2Tester} instance.
+ * @param resourceLoadClass the source class used to load resources
+ * @param type the type under test
+ * @param objectMapper the Jackson object mapper
+ * @param view the JSON view
+ */
+ public Jackson2Tester(Class> resourceLoadClass, ResolvableType type, ObjectMapper objectMapper,
+ @Nullable Class> view) {
+ super(resourceLoadClass, type);
+ Assert.notNull(objectMapper, "'objectMapper' must not be null");
+ this.objectMapper = objectMapper;
+ this.view = view;
+ }
+
+ @Override
+ protected JsonContent getJsonContent(String json) {
+ Configuration configuration = Configuration.builder()
+ .jsonProvider(new JacksonJsonProvider(this.objectMapper))
+ .mappingProvider(new JacksonMappingProvider(this.objectMapper))
+ .build();
+ Class> resourceLoadClass = getResourceLoadClass();
+ Assert.state(resourceLoadClass != null, "'resourceLoadClass' must not be null");
+ return new JsonContent<>(resourceLoadClass, getType(), json, configuration);
+ }
+
+ @Override
+ protected T readObject(InputStream inputStream, ResolvableType type) throws IOException {
+ return getObjectReader(type).readValue(inputStream);
+ }
+
+ @Override
+ protected T readObject(Reader reader, ResolvableType type) throws IOException {
+ return getObjectReader(type).readValue(reader);
+ }
+
+ private ObjectReader getObjectReader(ResolvableType type) {
+ ObjectReader objectReader = this.objectMapper.readerFor(getType(type));
+ if (this.view != null) {
+ return objectReader.withView(this.view);
+ }
+ return objectReader;
+ }
+
+ @Override
+ protected String writeObject(T value, ResolvableType type) throws IOException {
+ return getObjectWriter(type).writeValueAsString(value);
+ }
+
+ private ObjectWriter getObjectWriter(ResolvableType type) {
+ ObjectWriter objectWriter = this.objectMapper.writerFor(getType(type));
+ if (this.view != null) {
+ return objectWriter.withView(this.view);
+ }
+ return objectWriter;
+ }
+
+ private JavaType getType(ResolvableType type) {
+ return this.objectMapper.constructType(type.getType());
+ }
+
+ /**
+ * Utility method to initialize {@link Jackson2Tester} fields. See
+ * {@link Jackson2Tester class-level documentation} for example usage.
+ * @param testInstance the test instance
+ * @param objectMapper the JSON mapper
+ * @see #initFields(Object, ObjectMapper)
+ */
+ public static void initFields(Object testInstance, ObjectMapper objectMapper) {
+ new Jackson2FieldInitializer().initFields(testInstance, objectMapper);
+ }
+
+ /**
+ * Utility method to initialize {@link Jackson2Tester} fields. See
+ * {@link Jackson2Tester class-level documentation} for example usage.
+ * @param testInstance the test instance
+ * @param objectMapperFactory a factory to create the object mapper
+ * @see #initFields(Object, ObjectMapper)
+ */
+ public static void initFields(Object testInstance, ObjectFactory objectMapperFactory) {
+ new Jackson2FieldInitializer().initFields(testInstance, objectMapperFactory);
+ }
+
+ /**
+ * Returns a new instance of {@link Jackson2Tester} with the view that should be used
+ * for json serialization/deserialization.
+ * @param view the view class
+ * @return the new instance
+ */
+ public Jackson2Tester forView(Class> view) {
+ Class> resourceLoadClass = getResourceLoadClass();
+ ResolvableType type = getType();
+ Assert.state(resourceLoadClass != null, "'resourceLoadClass' must not be null");
+ Assert.state(type != null, "'type' must not be null");
+ return new Jackson2Tester<>(resourceLoadClass, type, this.objectMapper, view);
+ }
+
+ /**
+ * {@link FieldInitializer} for Jackson.
+ */
+ private static class Jackson2FieldInitializer extends FieldInitializer {
+
+ protected Jackson2FieldInitializer() {
+ super(Jackson2Tester.class);
+ }
+
+ @Override
+ protected AbstractJsonMarshalTester