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

Posted

技术标签:

【中文标题】如何使用 Java 返回部分 JSON 响应?【英文标题】:How to return a partial JSON response using Java? 【发布时间】:2012-03-08 01:19:16 【问题描述】:

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

一个示例可能如下所示:

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

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

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

这是我目前拥有的。它返回一个完整的用户对象。如何根据“字段”参数仅返回 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 服务方法以返回 String 而不是 User,如下所示:

@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】:

库 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>

【讨论】:

指向 3rd 方库和插件的链接通常不被 Stack Overflow 社区所接受。 SO 更倾向于获取代码示例以帮助您调试编程代码。不过,我确实喜欢你答案的精彩格式。 如果我想让字段查询参数选项不区分大小写怎么办?【参考方案3】:

在资源方法中为每个请求创建一个 ObjectMapper 实例可能会产生很大的性能开销。根据the Jackson performance best practices 的说法,对象映射器的创建成本很高。

相反,您可以使用 the Jackson 2.3 ObjectWriterModifier/ObjectReaderModifier feature 在资源方法中自定义 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"

【讨论】:

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

以上是关于如何使用 Java 返回部分 JSON 响应?的主要内容,如果未能解决你的问题,请参考以下文章

java语言,如何获取服务器响应发来的json网页(或代码)

当 HTTP 请求返回状态 401 时,如何在 Java 中解析响应正文

如何从 json 响应中制作动态部分标题

如何在 JSON 响应 ASP.NET Core 中关闭或处理 camelCasing?

如何以 JSON 形式返回 WCF 服务 POST 响应

Akka HTTP:如何将 Json 格式响应解组为域对象