Spring Web:禁用特定类的 Jackson 转换器

问题描述 投票:0回答:1

我正在开发一个项目,该项目严重依赖于 Json 序列化器、反序列化器的自定义实现以及将 json 对象表示为类。
我们就以

MyJsonClass
为例吧。

问题是,我真的需要使用自定义的

HttpMessageConverter
来转换为/从这种类型,因为杰克逊无法正确处理它。
在转换器列表中杰克逊自己的转换器之前或之后添加我自己的转换器似乎并不能阻止首先尝试杰克逊的转换器。这将记录此错误:

Failed to evaluate Jackson deserialization for type [... MyJsonClass ...]
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a deserializer for non-concrete Map type [...]

MyJsonClass
延伸
Map

能够真正阻止 Jackson 被尝试用于

application/json
http 调用,那就太好了。如果事情最终成功的话,我们不想在日志中出现这些无用的错误(因为最终使用了我们的
HttpMessageConverter
)。另一方面,我们确实需要杰克逊来处理任何不是
Map
的事情。

根据我所看到的自定义 Jackson 反序列化器/序列化器,它们不会让您完全控制完整的原始输入/输出(就像 HttpMessageConverters 所做的那样),而这正是我们使用 Json 类所需要的。

这就是我在配置类中注册自定义转换器的方式:

@SpringBootApplication
public class MyApplication implements WebMvcConfigurer {

    ...

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    
        // changing the index doesn't matter
        converters.add(new MyJsonClassHttpMessageConverter());
    
        WebMvcConfigurer.super.extendMessageConverters(converters);
    }
    
    @Bean
    RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
        List<HttpMessageConverter<?>> converters = 
                new LinkedList<>(new RestTemplate().getMessageConverters());
    
        // changing the index doesn't matter
        converters.add(new MyJsonClassHttpMessageConverter());
    
        return restTemplateBuilderConfigurer.configure(new RestTemplateBuilder())
                .messageConverters(converters);
    }

    ...

}
java spring spring-boot spring-mvc jackson
1个回答
0
投票

我找到了解决方案。

我创建了一个重写

MappingJackson2HttpMessageConverter
的类,将其添加到转换器列表中并删除了 Jackson 自己的转换器。
在此类的
canRead
canWrite
方法中,我检查 Type 是否为
MyJsonClass
,如果是,则返回 false。这会跳过使用 Jackson 来尝试转换,因此使用我自己的
HttpMessageConverter
来代替。

public class MyJsonClassIgnoringMappingJackson2HttpMessageConverter 
        extends MappingJackson2HttpMessageConverter {

    public MyJsonClassIgnoringMappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper);
    }

    @Override
    public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        if (contextClass != null && MyJsonClass.class.isAssignableFrom(contextClass)
                || type == MyJsonClass.class)
            return false;
        return super.canRead(type, contextClass, mediaType);
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        if (MyJsonClass.class.isAssignableFrom(clazz))
            return false;
        return super.canWrite(clazz, mediaType);
    }
}

(我确信上述检查可以改进,但对我来说它们是这样工作的)

配置类中:

@SpringBootApplication
public class MyApplication implements WebMvcConfigurer {

    @Autowired
    private ObjectMapper objectMapper; // let's use Jackson's properly-configured one

    ...

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    
        replaceJacksonConverters(converters);
    
        WebMvcConfigurer.super.extendMessageConverters(converters);
    }
    
    @Bean
    RestTemplateBuilder restTemplateBuilder(RestTemplateBuilderConfigurer restTemplateBuilderConfigurer) {
        List<HttpMessageConverter<?>> converters = 
                new LinkedList<>(new RestTemplate().getMessageConverters());
    
        replaceJacksonConverters(converters);
    
        return restTemplateBuilderConfigurer.configure(new RestTemplateBuilder())
                .messageConverters(converters);
    }

    ...

    private void replaceJacksonConverters(List<HttpMessageConverter<?>> converters) {

        for (int i = converters.size() - 1; i >= 0; --i) {
            if (converters.get(i) instanceof AbstractJackson2HttpMessageConverter)
                converters.remove(i);
        }
        // the custom HttpMessageConverter that can properly serialize and deserialize MyJsonClass
        converters.add(new MyJsonClassHttpMessageConverter());
        // the custom MappingJackson2HttpMessageConverter defined above
        converters.add(new MyJsonClassIgnoringMappingJackson2HttpMessageConverter(objectMapper));
    }

}

我没有将

MyJsonClassIgnoringMappingJackson2HttpMessageConverter
定义为
@Bean
的原因是,并非所有将原始类添加到列表中的 Spring 类都是通过将其作为 bean 来实现的。他们中的大多数人只是执行“new MappingJackson2HttpMessageConverter()”...所以因为我无论如何都需要替换它,就像我上面用那个
replaceJacksonConverters
方法所做的那样,我想我并不真正需要它作为一个 bean。

© www.soinside.com 2019 - 2024. All rights reserved.