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