【问题标题】:@JsonFilter throws "JsonMappingException: Can not resolve BeanPropertyFilter"@JsonFilter 抛出“JsonMappingException:无法解析 BeanPropertyFilter”
【发布时间】:2012-03-12 00:44:47
【问题描述】:

是否可以有选择地确定在运行时何时使用@JsonFilter 注解?

当我不提供过滤器时,我会收到 JsonMappingException 异常(见下文)。

背景:

我从recent StackOverflow post 中了解到,我可以使用@JsonFilter 来动态过滤被序列化的bean 属性。这很好用。将@JsonFilter("apiFilter") 添加到我的域类并在我的 jax-rs 服务中添加此代码(使用 CXF 实现)后,我能够动态过滤由我的 RESTful API 返回的属性:

// shortened for brevity
FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));

return mapper.filteredWriter(filters).writeValueAsString(user);

问题是我根本不想应用过滤器的不同服务调用。在这些情况下,我想返回整个域类而不过滤任何属性。在我只是尝试返回域类的情况下,我得到一个异常如下:

Caused by: org.codehaus.jackson.map.JsonMappingException: Can not resolve BeanPropertyFilter with id 'apiFilter'; no FilterProvider configured

at org.codehaus.jackson.map.ser.BeanSerializer.findFilter(BeanSerializer.java:252)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFieldsFiltered(BeanSerializer.java:216)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:140)

【问题讨论】:

    标签: json cxf jax-rs jackson


    【解决方案1】:

    我知道它已经得到了回答,但对于任何新来者来说,Jackson 实际上已经添加了在缺少过滤器时不会失败的功能 (JACKSON-650):
    你只需要打电话 SimpleFilterProvider.setFailOnUnknownId(false) 你不会得到这个异常。

    【讨论】:

    • 当然,即使您使用的映射器根本不打算使用过滤,您仍然需要配置 SimpleFilterProvider。哦,好吧。
    【解决方案2】:

    对于 Spring Boot / Jackson 配置只需添加:

    @Configuration 
    public class JacksonConfiguration { 
        public JacksonConfiguration(ObjectMapper objectMapper) { 
            objectMapper.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false)); 
        } 
    }
    

    【讨论】:

    • 这个解决方案正好解决了我的问题!
    【解决方案3】:

    我认为您可以欺骗已过滤的编写器,为您希望所有属性序列化的情况定义一个空的序列化过滤器:

    FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.serializeAllExcept(emptySet));
    

    这样,当引擎查找在@JsonFilter 注释中定义的“apiFilter”过滤器时,它会找到它,但它不会有任何效果(因为会序列化所有属性)。

    编辑 另外,你可以调用工厂方法writer()而不是filteredWriter()

    ObjectWriter writer=null;
    if(aplyFilter) {
        FilterProvider filters = new SimpleFilterProvider().addFilter("apiFilter", SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));
        writer=mapper.filteredWriter(filters);
    } else {
       writer=mapper.writer();
    }
    
    return writer.writeValueAsString(user);
    

    我认为最后一个解决方案更干净,而且确实更好。

    【讨论】:

    • 在您编辑的示例中,我是否需要包含代码来检查在每个 jax-rs 服务调用中调用哪个编写器方法?在某些服务方法中,我返回一个实际的用户对象而不是一个字符串。非常感谢您的意见!
    • 好的,我有机会试一试。您建议的“技巧”有效,但我无法让您的第二个“更清洁”建议起作用。在这种情况下,我仍然收到“未配置 FilterProvider”错误。再次感谢。
    • @Justin:好吧,IMO 解决问题的“不干净”解决方法比不工作的“干净”解决方法要好:)。希望它有助于解决您的问题。
    【解决方案4】:

    我遇到了类似的问题,得到了相同的异常,但接受的答案对我的情况并没有真正的帮助。这是对我有用的解决方案:

    在我的设置中,我使用了这样的自定义 JacksonSerializer:

    @JsonSerialize(using = MyCustomSerializer.class)
    private Object someAttribute;
    

    序列化器是这样实现的:

    public class MyCustomSerializer extends JsonSerializer<Object> {
      @Override
      public void serialize(Object o, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        if (o != null) {
          jgen.writeObject(o);
        }
      }
    }
    

    问题在于,只要您不使用任何过滤器,它就可以工作。如果您序列化原语,它也可以工作,例如,如果您使用jgen.writeString(..)。如果您使用过滤器,则该代码是错误的,因为过滤器存储在SerializerProvider 内部的某个位置,而不是JsonGenerator。如果在这种情况下您直接使用 jsongenerator,则会在内部创建一个不了解过滤器的新 SerializerProvider。因此,您需要调用provider.defaultSerializeValue(o, jgen),而不是较短的jgen.writeObject(o)。这将确保过滤器不会丢失并且可以应用。

    【讨论】:

      【解决方案5】:

      我已经应用了与提到的已接受解决方案相同的解决方案,但是当我将 writer.writeValueAsString(course) 作为 Rest 服务响应返回时,我会得到以下格式的响应

      { "status": "OK", "data": "[{\"name\":\"JPA in Use\",\"reviews\":[{\"id\":4081,\"rating\":\"4\",\"description\":\"Fine\"},{\"id\":4084,\"rating\":\"4\",\"description\":\"Ok\"}]},{\"name\":\"Spring in Use\",\"reviews\":[{\"id\":4003,\"rating\":\"3\",\"description\":\"Nice Course\"}]}]" }
      

      但我的预期反应是

      { "status": "OK", "data": [ { "name": "JPA in Use", "reviews": [ { "id": 4081, "rating": "4", "description": "Fine" }, { "id": 4082, "rating": "5", "description": "Great" } ] }, { "name": "Spring in Use", "reviews": [ { "id": 4003, "rating": "3", "description": "Nice Course" } ] } ] }
      

      为了得到我的回复,我已将 jsonstring 转换为特定的对象类型

      List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
      

      注意:课程有 id、name 和 reviews 作为字段,我想隐藏 id

      我正在提供代码 sn-p 希望它对某些人有所帮助。

      @GetMapping("/courses")
          public ResponseEntity<JpaResponse> allCourse() throws Exception {
              JpaResponse response = null;
               ObjectMapper mapper = new ObjectMapper(); 
               mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
              List<Course> course = service.findAllCourse();
              SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name","reviews");
              FilterProvider filterProvider = new SimpleFilterProvider().addFilter("jpafilter", filter).setFailOnUnknownId(false);
                      ObjectWriter writer = mapper.writer(filterProvider);
              String writeValueAsString = writer.writeValueAsString(course);
              List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
                  response = new JpaResponse(HttpStatus.OK.name(),resultcourse);
                  return new ResponseEntity<>(response, HttpStatus.OK);
      
      }
      
      
      public class JpaResponse {
              private String status;
              private Object data;
              public JpaResponse() {
                  super();
              }
              public JpaResponse(String status, Object data) {
                  super();
                  this.status = status;
                  this.data = data;
              }
      }
      

      【讨论】:

        【解决方案6】:

        这就是我为 Springboot 所做的,不再需要从应用程序中的所有 REST 响应中过滤这些字段的逻辑,如果您需要过滤更多 POJO,只需将它们添加到 FilterProvider:

        使用过滤器添加配置类:

        @Configuration
        public class JacksonConfiguration {
        
            public JacksonConfiguration(ObjectMapper objectMapper) {
                SimpleFilterProvider simpleFilterProvider = new SimpleFilterProvider();
                FilterProvider filters = simpleFilterProvider.addFilter("PojoFilterDTO",
                        SimpleBeanPropertyFilter.serializeAllExcept("field1", "field2")).setFailOnUnknownId(false);
                objectMapper.setFilterProvider(filters);
                objectMapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false);
            }
        }
        

        将 JsonFilter 注解添加到您的 POJO:

        @JsonInclude(JsonInclude.Include.NON_NULL)
        @JsonFilter("PojoFilterDTO")
        public class PojoDTO {
        }
        

        【讨论】:

          猜你喜欢
          • 2012-03-12
          • 2014-01-15
          • 1970-01-01
          • 2012-06-29
          • 1970-01-01
          • 2013-03-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多