全局杰克逊 ObjectMapper 未注册自定义序列化程序
Global jackson ObjectMapper not registering custom serializer
我正在使用 Java 17,spring-boot 2.6.3 与 spring-webflux 和 spring-consul 依赖项,我有以下 class:
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.reactive.config.EnableWebFlux;
import com.enterprise.project.model.serializer.ModelSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
@SpringBootApplication
@EnableWebFlux
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
static BeanFactoryPostProcessor beanFactoryPostProcessor(final ApplicationContext beanRegistry) {
return (final var beanFactory) -> {
final var beanDefinitionScanner = new ClassPathBeanDefinitionScanner(
(BeanDefinitionRegistry) ((AnnotationConfigReactiveWebServerApplicationContext) beanRegistry)
.getBeanFactory());
beanDefinitionScanner.addIncludeFilter(
(final var mr, final var mrf) -> !mr.getClassMetadata().getClassName().contains("model"));
beanDefinitionScanner.scan("com.enterprise.project", "com.enterprise.project.model.serializer");
};
}
@Bean
@Primary
public ObjectMapper customObjectMapper(final Jackson2ObjectMapperBuilder builder) {
return builder.serializationInclusion(JsonInclude.Include.NON_NULL).serializers(new ModelSerializer()).build();
}
}
这是我的自定义序列化程序。
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import com.enterprise.project.model.FirstModel;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class ModelSerializer extends StdSerializer<FirstModel> {
private static final long serialVersionUID = -5754330070183741830L;
public ModelSerializer() {
this(FirstModel.class);
}
public ModelSerializer(final Class<FirstModel> responseModel) {
super(responseModel);
}
@Override
public void serialize(final FirstModel Model, final JsonGenerator jgen,
@SuppressWarnings("unused") final SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeNumberField("one", Model.second());
final var totalValue = Model.third();
if (totalValue != null) {
jgen.writeNumberField("two", totalValue);
}
jgen.writeNumberField("three", Model.first().intValue());
jgen.writeStringField("four", Model.fourth());
final var surcharge = Model.fifth();
if (surcharge != null) {
jgen.writeBooleanField("five", surcharge.booleanValue());
}
jgen.writeStringField("six", ModelSerializer.encodeBase64(ModelSerializer.toXML(Model.sixth())));
jgen.writeEndObject();
}
public static String encodeBase64(final String toEncode) {
return Base64.getEncoder().encodeToString(toEncode.getBytes(StandardCharsets.UTF_8));
}
public static <T> String toXML(final T data) {
try {
return XmlMapper.builder().build().writeValueAsString(data);
} catch (@SuppressWarnings("unused") final JsonProcessingException e) {
return "";
}
}
}
我的模型就这么简单:
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public record FirstModel(Integer first, BigDecimal second, BigDecimal third, String fourth, Boolean fifth,
SecondModel sixth) {
// In most cases record classes does not need any implementation.
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "second")
@JsonInclude(JsonInclude.Include.NON_NULL)
public record SecondModel(@JacksonXmlProperty(isAttribute = true) String first,
@JacksonXmlProperty(isAttribute = true) String second, @JacksonXmlProperty(isAttribute = true) String third,
@JacksonXmlProperty(isAttribute = true) String fourth, @JacksonXmlProperty(isAttribute = true) String fifth,
@JacksonXmlProperty(isAttribute = true) String sixth, @JacksonXmlProperty(isAttribute = true) String seventh,
@JacksonXmlProperty(isAttribute = true) String eighth, @JacksonXmlProperty(isAttribute = true) String ninth,
@JacksonXmlProperty(isAttribute = true) String tenth, @JacksonXmlProperty(isAttribute = true) String eleventh,
@JacksonXmlProperty(isAttribute = true) ThirdModel twelfth) {
// In most cases record classes does not need any implementation.
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "third")
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ThirdModel(@JacksonXmlProperty(isAttribute = true) String first,
@JacksonXmlProperty(isAttribute = true) String second, @JacksonXmlProperty(isAttribute = true) String third,
@JacksonXmlProperty(isAttribute = true) String fourth, @JacksonXmlProperty(isAttribute = true) String fifth) {
// In most cases record classes does not need any implementation.
}
我不能用 @JsonSerialize(using = ModelSerializer.class)
注释我的模型 class 因为它处于我不允许修改的依赖项中。我知道我确实在这里写了代码模型,但我在这个存储库中只被授予只读权限。
即使我的 actuator/beans
和 actuator/conditions
端点将 customObjectMapper 报告为 bean,ModelSerializer
也永远不会被调用。任何想法为什么?我需要任何其他具体的杰克逊依赖项吗?
这是我的依赖树与 jackson 依赖关系:
com.enterprise.project:spring-webflux-custom-objectmapper:jar:1.0.0
+- org.springframework.boot:spring-boot-starter-actuator:jar:2.6.3:compile
| +- org.springframework.boot:spring-boot-starter:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot:jar:2.6.3:compile
| | | \- org.springframework:spring-context:jar:5.3.15:compile
| | | +- org.springframework:spring-aop:jar:5.3.15:compile
| | | \- org.springframework:spring-expression:jar:5.3.15:compile
| | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.3:compile
| | | +- ch.qos.logback:logback-classic:jar:1.2.10:compile
| | | | +- ch.qos.logback:logback-core:jar:1.2.10:compile
| | | | \- org.slf4j:slf4j-api:jar:1.7.33:compile
| | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.1:compile
| | | | \- org.apache.logging.log4j:log4j-api:jar:2.17.1:compile
| | | \- org.slf4j:jul-to-slf4j:jar:1.7.33:compile
| | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
| | +- org.springframework:spring-core:jar:5.3.15:compile
| | | \- org.springframework:spring-jcl:jar:5.3.15:compile
| | \- org.yaml:snakeyaml:jar:1.29:compile
| +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot-actuator:jar:2.6.3:compile
| | \- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.13.1:compile
| \- io.micrometer:micrometer-core:jar:1.8.2:compile
| +- org.hdrhistogram:HdrHistogram:jar:2.1.12:compile
| \- org.latencyutils:LatencyUtils:jar:2.0.3:runtime
+- org.springframework.boot:spring-boot-starter-webflux:jar:2.6.3:compile
| +- org.springframework.boot:spring-boot-starter-json:jar:2.6.3:compile
| | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.1:compile
| | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.13.1:compile
| +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.6.3:compile
| | \- io.projectreactor.netty:reactor-netty-http:jar:1.0.15:compile
| | +- io.netty:netty-codec-http:jar:4.1.73.Final:compile
| | | +- io.netty:netty-common:jar:4.1.73.Final:compile
| | | +- io.netty:netty-buffer:jar:4.1.73.Final:compile
| | | +- io.netty:netty-transport:jar:4.1.73.Final:compile
| | | +- io.netty:netty-codec:jar:4.1.73.Final:compile
| | | \- io.netty:netty-handler:jar:4.1.73.Final:compile
| | | \- io.netty:netty-tcnative-classes:jar:2.0.46.Final:compile
| | +- io.netty:netty-codec-http2:jar:4.1.73.Final:compile
| | +- io.netty:netty-resolver-dns:jar:4.1.73.Final:compile
| | | +- io.netty:netty-resolver:jar:4.1.73.Final:compile
| | | \- io.netty:netty-codec-dns:jar:4.1.73.Final:compile
| | +- io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.1.73.Final:compile
| | | \- io.netty:netty-resolver-dns-classes-macos:jar:4.1.73.Final:compile
| | +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.73.Final:compile
| | | +- io.netty:netty-transport-native-unix-common:jar:4.1.73.Final:compile
| | | \- io.netty:netty-transport-classes-epoll:jar:4.1.73.Final:compile
| | \- io.projectreactor.netty:reactor-netty-core:jar:1.0.15:compile
| | \- io.netty:netty-handler-proxy:jar:4.1.73.Final:compile
| | \- io.netty:netty-codec-socks:jar:4.1.73.Final:compile
| +- org.springframework:spring-web:jar:5.3.15:compile
| | \- org.springframework:spring-beans:jar:5.3.15:compile
| \- org.springframework:spring-webflux:jar:5.3.15:compile
| \- io.projectreactor:reactor-core:jar:3.4.14:compile
| \- org.reactivestreams:reactive-streams:jar:1.0.3:compile
\- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-core:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-databind:jar:2.13.1:compile
+- org.codehaus.woodstox:stax2-api:jar:4.2.1:compile
\- com.fasterxml.woodstox:woodstox-core:jar:6.2.7:compile
我也已经将代码放入 git repository to facilitate if someone can help me out and a issue report 到 spring-boot 存储库中。
您已经用 @EnableWebFlux
注释了您的应用程序。这表示您想要take complete control of WebFlux's configuration。这会导致 Spring 启动 WebFlux 的 auto-configuration 后退。除其他事项外,这意味着它不会将 WebFlux 配置为使用上下文的 ObjectMapper
.
您应该删除 @EnableWebFlux
以允许 Spring 引导至 auto-configure WebFlux,或者您应该手动配置其编解码器以便它们使用您的 ObjectMapper
.
我正在使用 Java 17,spring-boot 2.6.3 与 spring-webflux 和 spring-consul 依赖项,我有以下 class:
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.reactive.config.EnableWebFlux;
import com.enterprise.project.model.serializer.ModelSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
@SpringBootApplication
@EnableWebFlux
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
static BeanFactoryPostProcessor beanFactoryPostProcessor(final ApplicationContext beanRegistry) {
return (final var beanFactory) -> {
final var beanDefinitionScanner = new ClassPathBeanDefinitionScanner(
(BeanDefinitionRegistry) ((AnnotationConfigReactiveWebServerApplicationContext) beanRegistry)
.getBeanFactory());
beanDefinitionScanner.addIncludeFilter(
(final var mr, final var mrf) -> !mr.getClassMetadata().getClassName().contains("model"));
beanDefinitionScanner.scan("com.enterprise.project", "com.enterprise.project.model.serializer");
};
}
@Bean
@Primary
public ObjectMapper customObjectMapper(final Jackson2ObjectMapperBuilder builder) {
return builder.serializationInclusion(JsonInclude.Include.NON_NULL).serializers(new ModelSerializer()).build();
}
}
这是我的自定义序列化程序。
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import com.enterprise.project.model.FirstModel;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
public class ModelSerializer extends StdSerializer<FirstModel> {
private static final long serialVersionUID = -5754330070183741830L;
public ModelSerializer() {
this(FirstModel.class);
}
public ModelSerializer(final Class<FirstModel> responseModel) {
super(responseModel);
}
@Override
public void serialize(final FirstModel Model, final JsonGenerator jgen,
@SuppressWarnings("unused") final SerializerProvider provider) throws IOException {
jgen.writeStartObject();
jgen.writeNumberField("one", Model.second());
final var totalValue = Model.third();
if (totalValue != null) {
jgen.writeNumberField("two", totalValue);
}
jgen.writeNumberField("three", Model.first().intValue());
jgen.writeStringField("four", Model.fourth());
final var surcharge = Model.fifth();
if (surcharge != null) {
jgen.writeBooleanField("five", surcharge.booleanValue());
}
jgen.writeStringField("six", ModelSerializer.encodeBase64(ModelSerializer.toXML(Model.sixth())));
jgen.writeEndObject();
}
public static String encodeBase64(final String toEncode) {
return Base64.getEncoder().encodeToString(toEncode.getBytes(StandardCharsets.UTF_8));
}
public static <T> String toXML(final T data) {
try {
return XmlMapper.builder().build().writeValueAsString(data);
} catch (@SuppressWarnings("unused") final JsonProcessingException e) {
return "";
}
}
}
我的模型就这么简单:
import java.math.BigDecimal;
import com.fasterxml.jackson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_NULL)
public record FirstModel(Integer first, BigDecimal second, BigDecimal third, String fourth, Boolean fifth,
SecondModel sixth) {
// In most cases record classes does not need any implementation.
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "second")
@JsonInclude(JsonInclude.Include.NON_NULL)
public record SecondModel(@JacksonXmlProperty(isAttribute = true) String first,
@JacksonXmlProperty(isAttribute = true) String second, @JacksonXmlProperty(isAttribute = true) String third,
@JacksonXmlProperty(isAttribute = true) String fourth, @JacksonXmlProperty(isAttribute = true) String fifth,
@JacksonXmlProperty(isAttribute = true) String sixth, @JacksonXmlProperty(isAttribute = true) String seventh,
@JacksonXmlProperty(isAttribute = true) String eighth, @JacksonXmlProperty(isAttribute = true) String ninth,
@JacksonXmlProperty(isAttribute = true) String tenth, @JacksonXmlProperty(isAttribute = true) String eleventh,
@JacksonXmlProperty(isAttribute = true) ThirdModel twelfth) {
// In most cases record classes does not need any implementation.
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
@JacksonXmlRootElement(localName = "third")
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ThirdModel(@JacksonXmlProperty(isAttribute = true) String first,
@JacksonXmlProperty(isAttribute = true) String second, @JacksonXmlProperty(isAttribute = true) String third,
@JacksonXmlProperty(isAttribute = true) String fourth, @JacksonXmlProperty(isAttribute = true) String fifth) {
// In most cases record classes does not need any implementation.
}
我不能用 @JsonSerialize(using = ModelSerializer.class)
注释我的模型 class 因为它处于我不允许修改的依赖项中。我知道我确实在这里写了代码模型,但我在这个存储库中只被授予只读权限。
即使我的 actuator/beans
和 actuator/conditions
端点将 customObjectMapper 报告为 bean,ModelSerializer
也永远不会被调用。任何想法为什么?我需要任何其他具体的杰克逊依赖项吗?
这是我的依赖树与 jackson 依赖关系:
com.enterprise.project:spring-webflux-custom-objectmapper:jar:1.0.0
+- org.springframework.boot:spring-boot-starter-actuator:jar:2.6.3:compile
| +- org.springframework.boot:spring-boot-starter:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot:jar:2.6.3:compile
| | | \- org.springframework:spring-context:jar:5.3.15:compile
| | | +- org.springframework:spring-aop:jar:5.3.15:compile
| | | \- org.springframework:spring-expression:jar:5.3.15:compile
| | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.3:compile
| | | +- ch.qos.logback:logback-classic:jar:1.2.10:compile
| | | | +- ch.qos.logback:logback-core:jar:1.2.10:compile
| | | | \- org.slf4j:slf4j-api:jar:1.7.33:compile
| | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.1:compile
| | | | \- org.apache.logging.log4j:log4j-api:jar:2.17.1:compile
| | | \- org.slf4j:jul-to-slf4j:jar:1.7.33:compile
| | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
| | +- org.springframework:spring-core:jar:5.3.15:compile
| | | \- org.springframework:spring-jcl:jar:5.3.15:compile
| | \- org.yaml:snakeyaml:jar:1.29:compile
| +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:2.6.3:compile
| | +- org.springframework.boot:spring-boot-actuator:jar:2.6.3:compile
| | \- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.13.1:compile
| \- io.micrometer:micrometer-core:jar:1.8.2:compile
| +- org.hdrhistogram:HdrHistogram:jar:2.1.12:compile
| \- org.latencyutils:LatencyUtils:jar:2.0.3:runtime
+- org.springframework.boot:spring-boot-starter-webflux:jar:2.6.3:compile
| +- org.springframework.boot:spring-boot-starter-json:jar:2.6.3:compile
| | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.1:compile
| | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.13.1:compile
| +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.6.3:compile
| | \- io.projectreactor.netty:reactor-netty-http:jar:1.0.15:compile
| | +- io.netty:netty-codec-http:jar:4.1.73.Final:compile
| | | +- io.netty:netty-common:jar:4.1.73.Final:compile
| | | +- io.netty:netty-buffer:jar:4.1.73.Final:compile
| | | +- io.netty:netty-transport:jar:4.1.73.Final:compile
| | | +- io.netty:netty-codec:jar:4.1.73.Final:compile
| | | \- io.netty:netty-handler:jar:4.1.73.Final:compile
| | | \- io.netty:netty-tcnative-classes:jar:2.0.46.Final:compile
| | +- io.netty:netty-codec-http2:jar:4.1.73.Final:compile
| | +- io.netty:netty-resolver-dns:jar:4.1.73.Final:compile
| | | +- io.netty:netty-resolver:jar:4.1.73.Final:compile
| | | \- io.netty:netty-codec-dns:jar:4.1.73.Final:compile
| | +- io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.1.73.Final:compile
| | | \- io.netty:netty-resolver-dns-classes-macos:jar:4.1.73.Final:compile
| | +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.73.Final:compile
| | | +- io.netty:netty-transport-native-unix-common:jar:4.1.73.Final:compile
| | | \- io.netty:netty-transport-classes-epoll:jar:4.1.73.Final:compile
| | \- io.projectreactor.netty:reactor-netty-core:jar:1.0.15:compile
| | \- io.netty:netty-handler-proxy:jar:4.1.73.Final:compile
| | \- io.netty:netty-codec-socks:jar:4.1.73.Final:compile
| +- org.springframework:spring-web:jar:5.3.15:compile
| | \- org.springframework:spring-beans:jar:5.3.15:compile
| \- org.springframework:spring-webflux:jar:5.3.15:compile
| \- io.projectreactor:reactor-core:jar:3.4.14:compile
| \- org.reactivestreams:reactive-streams:jar:1.0.3:compile
\- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-core:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.1:compile
+- com.fasterxml.jackson.core:jackson-databind:jar:2.13.1:compile
+- org.codehaus.woodstox:stax2-api:jar:4.2.1:compile
\- com.fasterxml.woodstox:woodstox-core:jar:6.2.7:compile
我也已经将代码放入 git repository to facilitate if someone can help me out and a issue report 到 spring-boot 存储库中。
您已经用 @EnableWebFlux
注释了您的应用程序。这表示您想要take complete control of WebFlux's configuration。这会导致 Spring 启动 WebFlux 的 auto-configuration 后退。除其他事项外,这意味着它不会将 WebFlux 配置为使用上下文的 ObjectMapper
.
您应该删除 @EnableWebFlux
以允许 Spring 引导至 auto-configure WebFlux,或者您应该手动配置其编解码器以便它们使用您的 ObjectMapper
.