@JsonFilter抛出“JsonMappingException:无法解析BeanPropertyFilter”

时间:2012-02-21 17:22:30

标签: json cxf jax-rs jackson

是否可以有选择地确定@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)

5 个答案:

答案 0 :(得分:23)

我知道它已经得到了解答但是对于任何新手来说,杰克逊实际上已经添加了在失踪过滤器上失败的能力(JACKSON-650):
你只需要打电话 SimpleFilterProvider.setFailOnUnknownId(false)你不会得到这个例外。

答案 1 :(得分:6)

我认为你可以欺骗过滤的编写器定义一个空的序列化过滤器,用于你想要所有属性被分类的情况:

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

这样,当引擎查找在@JsonFilter anotation中定义的“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);

我认为最后的解决方案更清洁,更确切。

答案 2 :(得分:3)

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

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

答案 3 :(得分:0)

我有一个类似的问题得到相同的例外,但接受的答案在我的情况下并没有真正帮助。这是对我有用的解决方案:

在我的设置中,我使用了这样的自定义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。因此,您需要调用jgen.writeObject(o)而不是较短的provider.defaultSerializeValue(o, jgen)。这将确保过滤器不会丢失并且可以应用。

答案 4 :(得分:0)

我已经应用了与提到的已接受解决方案相同的解决方案,但是当我将 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、名称和评论作为字段,我想取消 ID

我提供了代码片段,希望对某些人有所帮助。

@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;
        }
}