Spring 4和Jackson 2:某些类未使用ObjectMapper配置。

huangapple go评论113阅读模式
英文:

Spring 4 & Jackson 2: ObjectMapper Configuration Not Used For Some Classes

问题

我正尝试通过代码配置我的MVC应用程序的ObjectMapper。我遇到的问题是,它似乎在序列化某些类时遵循配置,但在其他类上不遵循。在我具体的情况下,ObjectMapper似乎没有使用的类是一个Hibernate实体。

这是配置的简化版本:

@Configuration
@EnableWebMvc
public class WebMvcDefaultObjectMapperConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> httpConverter : converters) {
            if (httpConverter instanceof MappingJackson2HttpMessageConverter) {
                ((MappingJackson2HttpMessageConverter) httpConverter).setObjectMapper(configureMapper());
            }
        }
    }

    @Primary
    @Bean
    public ObjectMapper defaultMapper() {
        return configureMapper();
    }

    private ObjectMapper configureMapper() {
        ObjectMapper mapper = new ObjectMapper();

        PropertyFilter filter = new SimpleBeanPropertyFilter() {...};
        FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter", filter);

        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.setFilterProvider(filters);
        
        return mapper;
    }
}

我遇到的确切错误是,我有一个特定的类配置为使用过滤器(myFilter),但当它被序列化时,它会抛出一个JsonMappingException,指出“无法解析ID为'myFilter'的PropertyFilter;没有配置FilterProvider...”。这让我认为我的配置是“错的地方”,Spring没有使用它。

但是,我有相反的证据。当发生错误时,我的API会返回一个JSON错误消息,如果我更改上述配置代码中INDENT_OUTPUT序列化特性的值,它将反映在该错误消息中(即错误消息将以漂亮的格式打印或不打印)。所以至少对于一些类来说是有效的。

有人知道是什么原因导致某个类以某种其他的映射器序列化吗?

其他注意事项/我尝试过的事情:
  • 从配置类中添加/删除@EnableWebMvc不会有任何区别。无论哪种方式都会导致相同的错误。
  • 我尝试过通过几乎每种途径配置映射器,例如MappingJackson2HttpMessageConverter bean,HttpMessageConverters bean,Jackson2ObjectMapperBuilder bean,普通的ObjectMapper bean。无论如何都是相同的错误。
  • 我还尝试过一个RestTemplate bean,尽管我不确定我实际上是否在任何地方使用它。
英文:

I'm attempting to configure my MVC application's ObjectMapper via code. The issue I'm running into is that it seems to be respecting the configuration when serializing some classes, but not others. In my specific case the class where the ObjectMapper seems to not be used is a Hibernate entity.

Here's a simplified version of the config:

@Configuration
@EnableWebMvc
public class WebMvcDefaultObjectMapperConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void extendMessageConverters(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters) {
        for (HttpMessageConverter&lt;?&gt; httpConverter : converters) {
            if (httpConverter instanceof MappingJackson2HttpMessageConverter) {
                ((MappingJackson2HttpMessageConverter) httpConverter).setObjectMapper(configureMapper());
            }
        }
    }

    @Primary
    @Bean
    public ObjectMapper defaultMapper() {
        return configureMapper();
    }

    private ObjectMapper configureMapper() {
        ObjectMapper mapper = new ObjectMapper();

        PropertyFilter filter = new SimpleBeanPropertyFilter() {...};
        FilterProvider filters = new SimpleFilterProvider().addFilter(&quot;myFilter&quot;, filter);

        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.setFilterProvider(filters);
        
        return mapper;
    }
}

The exact error I'm running in to is that I have a specific class configured to use a filter (myFilter) but when it gets serialized it throws a JsonMappingException saying &quot;Can not resolve PropertyFilter with id &#39;myFilter&#39;; no FilterProvider configured...&quot;. That would make me think that my configuration is "in the wrong place" and Spring isn't picking it up.

However, I have evidence to the contrary. When an error occurs, my API returns a JSON error message and if I change the value of the INDENT_OUTPUT serialization feature in the above configuration code, it's reflected in that error message (i.e. the error will be pretty printed or not). So it's working for at least some classes.

Does anyone know what can cause a class to somehow be serialized using some other mapper?

Other notes/things I've tried:
  • Adding/removing the @EnableWebMvc from the configuration class makes no difference. It fails with the same error either way.
  • I've tried configuring the mapper via just about every avenue, i.e. a MappingJackson2HttpMessageConverter bean, HttpMessageConverters bean, Jackson2ObjectMapperBuilder bean, plain old ObjectMapper bean. It's the same error no matter what.
  • I've also tried a RestTemplate bean, although I'm not sure I'm actually using that anywhere.

答案1

得分: 1

经过大量的调试,一些其他奇怪的行为引导我找到了解决方案。

原来,对于不同的API调用,同一个Hibernate实体会被序列化,没有问题。这是因为该端点函数如下:

public @ResponseBody List<MyEntity> listEntity() {
    List<MyEntity> entities = ...;
    return entities;
}

但是出错的端点看起来像这样:

public String getEntities(Model model) {
    List<MyEntity> entities = ...;
    model.addAttribute("success", Boolean.TRUE);
    model.addAttribute("entities", entities);
    
    return "jsonTemplate";
}

不同之处在于出错的端点在使用jsonTemplate视图,而非出错的端点没有。事实证明,在我定义这个视图时,我没有配置它来使用我的自定义ObjectMapper。将其更新为这样可以解决问题。

最终解决方案

@Bean
public View jsonTemplate() {
    MappingJackson2JsonView view = new MappingJackson2JsonView();
    ObjectMapper myMapper = configureMapper();
    view.setObjectMapper(myMapper);
    return view;
}
英文:

After much debugging, some other strange behavior lead me to the solution.

It turned out that for a different API call, the same hibernate entity would be serialized no problem. The reason for this was because that endpoint function looked like this:

public @ResponseBody List&lt;MyEntity&gt; listEntity() {
    List&lt;MyEntity&gt; entities = ...;
    return entities;
}

But the erroring endpoint looked like this:

public String getEntities(Model model) {
    List&lt;MyEntity&gt; entities = ...;
    model.addAttribute(&quot;success&quot;, Boolean.TRUE);
    model.addAttribute(&quot;entities&quot;, entities);
    
    return &quot;jsonTemplate&quot;;
}

The difference is that the erroring endpoint was using the jsonTemplate view while the non-erroring one was not. It turns out that in my definition of this view, I wasn't configuring it to use my custom ObjectMapper. Updating it to do so fixed the problem.

The Final Solution

@Bean
public View jsonTemplate() {
    MappingJackson2JsonView view = new MappingJackson2JsonView();
    ObjectMapper myMapper = configureMapper();
    view.setObjectMapper(myMapper);
    return view;
}

huangapple
  • 本文由 发表于 2023年8月4日 02:56:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76830933.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定