我正在开发一个项目,该项目严重依赖于 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);
}
...
}
我找到了解决方案。
我创建了一个重写
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。