如何使用Java返回部分JSON响应?

2022-09-01 01:39:25

我正在构建一个RESTful API,并希望为开发人员提供选择在JSON响应中返回哪些字段的选项。这篇博客文章展示了几个API(Google,Facebook,LinkedIn)如何允许开发人员自定义响应的示例。这称为部分响应。

示例可能如下所示:

/users/123?fields=userId,fullname,title

在上面的示例中,API 应返回用户“123”的 userId、fullName 和 title 字段。

我正在寻找如何在我的RESTful Web服务中实现这一点的想法。我目前正在使用CXF(编辑:和Jackson),但愿意尝试另一个JAX-RS实现。

这是我目前拥有的。它返回一个完整的 User 对象。如何基于“字段”参数在运行时仅返回 API 调用方想要的字段?我不想将其他字段设为 Null。我根本不想归还它们。

@GET
@Path("/{userId}")
@Produces("application/json")
public User getUser(@PathParam("userId") Long userId, 
    @DefaultValue("userId,fullname,title") @QueryParam("fields") String fields) {

User user = userService.findOne(userId);

StringTokenizer st = new StringTokenizer(fields, ",");
while (st.hasMoreTokens()) {

    // here's where i would like to select only the fields i want to return

}
return user;
}

更新:

我点击了unludo的链接,然后链接到这个:http://wiki.fasterxml.com/JacksonFeatureJsonFilter

有了这些信息,我添加到了我的域名类。然后,我修改了我的RESTful服务方法以返回字符串而不是用户,如下所示:@JsonFilter("myFilter")

@GET
@Path("/{userId}")
@Produces("application/json")
public String getUser(@PathParam("userId") Long userId,
                    @DefaultValue("userId,fullname,title") @QueryParam("fields") String fields) {

    User user = userService.findOne(userId);

    StringTokenizer st = new StringTokenizer(fields, ",");
    Set<String> filterProperties = new HashSet<String>();
    while (st.hasMoreTokens()) {
        filterProperties.add(st.nextToken());
    }

    ObjectMapper mapper = new ObjectMapper();
    FilterProvider filters = new SimpleFilterProvider().addFilter("myFilter",
                SimpleBeanPropertyFilter.filterOutAllExcept(filterProperties));

    try {
        String json = mapper.filteredWriter(filters).writeValueAsString(user);
        return json;
    } catch (IOException e) {
        e.printStackTrace();
    return e.getMessage();
    }
}

我需要做更多的测试,但到目前为止一切都很好。


答案 1

如果你使用Jackson(一个很棒的JSON库 - 我相信是Java的标准),你可以使用注释来过滤结果对象中你想要的内容。@View

我知道你想要一些动态的东西,所以它有点复杂。您将在这里找到您要找的内容:http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html(查看6。完全动态过滤:)。@JsonFilter

我会对你会发现的解决方案感兴趣。


答案 2

在资源方法内为每个请求创建 ObjectMapper 实例可能会产生显著的性能开销。根据Jackson的性能最佳实践,对象映射器的创建成本很高。

相反,您可以使用 Jackson 2.3 ObjectWriterModifier/ObjectReaderModifier 功能在资源方法中自定义 JAX-RS 提供程序的 Jackson 对象编写器。

下面是一个示例,演示如何注册一个 ObjectWriterModifier 线程本地对象,该对象更改应用于资源方法中使用的 JAX-RS Jackson 提供程序的过滤器集。请注意,我还没有针对 JAX-RS 实现测试代码。

public class JacksonObjectWriterModifier2 {

    private static class FilterModifier extends ObjectWriterModifier {
        private final FilterProvider provider;

        private FilterModifier(FilterProvider provider) {
            this.provider = provider;
        }

        @Override
        public ObjectWriter modify(EndpointConfigBase<?> endpoint, MultivaluedMap<String, Object> responseHeaders,
                                   Object valueToWrite, ObjectWriter w, JsonGenerator g) throws IOException {
            return w.with(provider);
        }
    }

    @JsonFilter("filter1")
    public static class Bean {
        public final String field1;
        public final String field2;

        public Bean(String field1, String field2) {
            this.field1 = field1;
            this.field2 = field2;
        }
    }

    public static void main(String[] args) throws IOException {
        Bean b = new Bean("a", "b");
        JacksonJsonProvider provider = new JacksonJsonProvider();
        ObjectWriterInjector.set(new FilterModifier(new SimpleFilterProvider().addFilter("filter1",
                SimpleBeanPropertyFilter.filterOutAllExcept("field1"))));

        provider.writeTo(b, Bean.class, null, null, MediaType.APPLICATION_JSON_TYPE, null, System.out);
    }

}

输出:

{"field1":"a"}