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

发布于 2025-01-07 13:03:06 字数 1172 浏览 4 评论 0原文

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

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

背景:

我从最近的StackOverflow 帖子 说我可以使用 @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)

Is it possible to selectively determine when the @JsonFilter annotation gets used at runtime?

I'm getting JsonMappingException exception (see below) when I don't provide the filter.

Background:

I learned from a recent StackOverflow post that I can use @JsonFilter to dynamically filter the bean properties getting serialized. This works great. After adding @JsonFilter("apiFilter") to my domain class and with the addition of this code in my jax-rs service (using the CXF implementation), I am able to dynamically filter the properties returned by my RESTful API:

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

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

The problem is there are different service calls where I don't want to apply the filter at all. In those cases I want to return the entire domain class without filtering any properties. In the case where I just try to return the domain class I'm getting an exception as follows:

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

朦胧时间 2025-01-14 13:03:06

我知道这个问题已经得到解答,但对于任何新人来说,杰克逊实际上添加了不会因缺少过滤器而失败的功能(JACKSON-650):
你只需要打电话
SimpleFilterProvider.setFailOnUnknownId(false) 并且您不会收到此异常。

I know it's already been answered but for any newcommers Jackson has actually added the ability to not fail on missing filters (JACKSON-650):
You just need to call
SimpleFilterProvider.setFailOnUnknownId(false) and you won't get this exception.

黎夕旧梦 2025-01-14 13:03:06

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

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

For Spring Boot / Jackson configuration just add:

@Configuration 
public class JacksonConfiguration { 
    public JacksonConfiguration(ObjectMapper objectMapper) { 
        objectMapper.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false)); 
    } 
}
素衣风尘叹 2025-01-14 13:03:06

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

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);

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

I think you could trick the filtered writer defining an empty serialize filter for the cases where you want all the properties seralized:

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

This way, when the engine looks for the "apiFilter" filter defined at the @JsonFilter anotation, it finds it, but it will not have any effect (as will serialize all the properties).

EDIT
Also, you can call the factory method writer() instead of 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);

I think this last solution is way cleaner, and indeed better.

秋日私语 2025-01-14 13:03:06

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

在我的设置中,我使用像这样的自定义 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)。这将确保过滤器不会丢失并且可以应用。

I had a similar issue getting the same Exception, but the accepted answer didn't really help in my case. Here's the solution that worked for me:

In my setup I was using a custom JacksonSerializer like this:

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

And that serializer was implemented like this:

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

The problem with this is, that as long as you don't use any filters, it works. It also works if you serialize primitives, so for instance if you use jgen.writeString(..). If you use filters, that code is wrong, because the filters are stored somewhere inside of the SerializerProvider, not in the JsonGenerator. If in that case you use the jsongenerator directly, a new SerializerProvider, that doesn't know about the filters, is created internally. So instead of the shorter jgen.writeObject(o) you need to call provider.defaultSerializeValue(o, jgen). That will ensure that the filters don't get lost and can be applied.

梦开始←不甜 2025-01-14 13:03:06

这就是我为 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);
    }
}

添加POJO 的 JsonFilter 注释:

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

This is what I did for Springboot, no more logic is needed to filter those fields from all the REST responses in your application, if you need to filter more POJOs just add them to the FilterProvider:

Add a configuration class with the filter:

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

Add the JsonFilter annotation to your POJO:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonFilter("PojoFilterDTO")
public class PojoDTO {
}
煞人兵器 2025-01-14 13:03:06

我已经应用了与上述接受的解决方案相同的解决方案,但是当我返回 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;
        }
}

I have applied the same solution as mentioned accepted solution but when i am returning writer.writeValueAsString(course) as a Rest service response then i am getting response in below format

{ "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\"}]}]" }

But My expected Response is

{ "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" } ] } ] }

For getting my response i have applied converted the jsonstring to specfic object type

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

Note: Course has id,name and reviews as field and i want to suppress the id

I am providing the code snippet hope it is helpful to some.

@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;
        }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文