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

43
我正在构建一个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 对象。如何根据运行时的“fields”参数仅返回 API 调用者想要的字段?我不想使其他字段为空。我只是不想返回它们。
@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

有了这些信息,我在我的域类中添加了@JsonFilter("myFilter")。然后,我修改了我的RESTful服务方法,将返回类型从User改为String,如下所示:

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

我需要进行更多的测试,但目前为止还不错。


如果我想使字段查询参数选项不区分大小写怎么办? - Anushree Acharjee
3个回答

19
如果你使用Jackson(一款非常棒的JSON库 - 我认为在Java中是标准),你可以使用 @View 注释来过滤所需的结果对象。
我理解你想要的是一些动态的内容,因此稍微有些复杂。您可以在这里找到您要查找的内容: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html (查看 6. Fully dynamic filtering: @JsonFilter)。
我很想了解您会发现什么样的解决方案。

如果我想使字段查询参数选项不区分大小写怎么办? - Anushree Acharjee

5
在每个请求中为资源方法创建一个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"}

如果我想使字段查询参数选项不区分大小写怎么办? - Anushree Acharjee

4

这个名为 jersey-entity-filtering 的库可以实现以下功能:

https://github.com/jersey/jersey/tree/2.22.2/examples/entity-filtering-selectable

https://jersey.java.net/documentation/latest/entity-filtering.html

示例:

我的对象

public class Address {

    private String streetAddress;
    private String region;
    private PhoneNumber phoneNumber;
}

网址

people/1234?select=streetAddress,region

返回

{
   "streetAddress": "2 square Tyson",
   "region": "Texas"
}

添加到Maven

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-entity-filtering</artifactId>
    <version>2.22.2</version>
</dependency>

第三方库和插件的链接通常不被 Stack Overflow 社区所认可。SO 更倾向于提供代码示例来帮助您调试编程代码。但我确实喜欢您回答的美妙格式。 - Kmeixner
如果我想使字段查询参数选项不区分大小写怎么办? - Anushree Acharjee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接