Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ dependencies {
recipeDependencies {
parserClasspath("com.fasterxml.jackson.core:jackson-annotations:2.19.2")
parserClasspath("com.fasterxml.jackson.core:jackson-core:2.19.2")
parserClasspath("com.fasterxml.jackson.core:jackson-databind:2.19.2")
parserClasspath("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.3")
parserClasspath("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.3")

parserClasspath("tools.jackson.core:jackson-core:3.+")
parserClasspath("tools.jackson.core:jackson-databind:3.+")

testParserClasspath("com.fasterxml.jackson.core:jackson-databind:2.19.2")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.java.jackson;

import org.jspecify.annotations.Nullable;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;

public class UseFormatAlignedObjectMappers extends Recipe {

private static final MethodMatcher OBJECT_MAPPER_FACTORY = new MethodMatcher("com.fasterxml.jackson.databind.ObjectMapper <constructor>(com.fasterxml.jackson.core.JsonFactory)");

@Override
public String getDisplayName() {
return "Use format alignment `ObjectMappers`";
}

@Override
public String getDescription() {
return "Replace wrapping `ObjectMapper` calls with their format aligned implementation.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) {
J.NewClass nc = super.visitNewClass(newClass, ctx);

if (!OBJECT_MAPPER_FACTORY.matches(nc)) {
return nc;
}

JavaTemplate explicitMapperTemplate = createExplicitMapperTemplate(nc.getArguments().get(0), ctx);
if (explicitMapperTemplate == null) { // unsupported migration
return nc;
}

return explicitMapperTemplate.apply(getCursor(), nc.getCoordinates().replace());
}

private @Nullable JavaTemplate createExplicitMapperTemplate(Expression factory, ExecutionContext ctx) {
JavaType type = factory.getType();

if (TypeUtils.isAssignableTo("com.fasterxml.jackson.dataformat.yaml.YAMLFactory", type)) {
maybeRemoveImport("com.fasterxml.jackson.dataformat.yaml.YAMLFactory");
maybeAddImport("com.fasterxml.jackson.dataformat.yaml.YAMLMapper");
return JavaTemplate.builder("new YAMLMapper()")
.imports("com.fasterxml.jackson.dataformat.yaml.YAMLMapper")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "jackson-core-2", "jackson-databind-2", "jackson-dataformat-yaml-2"))
.build();
}

if (TypeUtils.isAssignableTo("com.fasterxml.jackson.dataformat.xml.XmlFactory", type)) {
maybeRemoveImport("com.fasterxml.jackson.dataformat.xml.XmlFactory");
maybeAddImport("com.fasterxml.jackson.dataformat.xml.XmlMapper");
return JavaTemplate.builder("new XmlMapper()")
.imports("com.fasterxml.jackson.dataformat.xml.XmlMapper")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "jackson-core-2", "jackson-databind-2", "jackson-dataformat-xml-2"))
.build();
}

if (TypeUtils.isAssignableTo("com.fasterxml.jackson.core.JsonFactory", type)) {
// we default back to JSON as it's the Jackson default
maybeRemoveImport("com.fasterxml.jackson.core.JsonFactory");
maybeAddImport("com.fasterxml.jackson.databind.json.JsonMapper");
return JavaTemplate.builder("new JsonMapper()")
.imports("com.fasterxml.jackson.databind.json.JsonMapper")
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "jackson-core-2", "jackson-databind-2"))
.build();
}

return null; // unsupported factory type
}
};
}
}
1 change: 1 addition & 0 deletions src/main/resources/META-INF/rewrite/jackson-2-3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ description: >-
tags:
- jackson-3
recipeList:
- org.openrewrite.java.jackson.UseFormatAlignedObjectMappers
- org.openrewrite.java.jackson.UpgradeJackson_2_3_Dependencies
- org.openrewrite.java.jackson.UpgradeJackson_2_3_TypeChanges
- org.openrewrite.java.jackson.UpgradeJackson_2_3_MethodRenames
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2025 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.openrewrite.java.jackson;

import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

class UseFormatAlignedObjectMappersTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec.recipe(new UseFormatAlignedObjectMappers())
.parser(JavaParser.fromJavaVersion()
.logCompilationWarningsAndErrors(true)
.classpathFromResources(new InMemoryExecutionContext(),
"jackson-core-2", "jackson-databind-2", "jackson-dataformat-yaml-2", "jackson-dataformat-xml-2"));
}

@Test
void keepObjectMapper() {
rewriteRun(
java(
"""
import com.fasterxml.jackson.databind.ObjectMapper;

class A {
ObjectMapper mapper = new ObjectMapper();
}
"""
)
);
}

@DocumentExample
@Test
void jsonMapper() {
rewriteRun(
java(
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonFactory;

class A {
ObjectMapper mapper = new ObjectMapper(new JsonFactory());
}
""",
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;

class A {
ObjectMapper mapper = new JsonMapper();
}
"""
)
);
}

@Test
void xmlMapper() {
rewriteRun(
java(
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;

class A {
ObjectMapper mapper = new ObjectMapper(new XmlFactory());
}
""",
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;

class A {
ObjectMapper mapper = new XmlMapper();
}
"""
)
);
}

@Test
void ymlMapper() {
rewriteRun(
java(
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;

class A {
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
}
""",
"""
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;

class A {
ObjectMapper mapper = new YAMLMapper();
}
"""
)
);
}
}
Binary file modified src/test/resources/META-INF/rewrite/classpath.tsv.gz
Binary file not shown.