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

发布于 2025-01-06 10:48:38 字数 2258 浏览 2 评论 0原文

我正在构建一个 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 服务方法以返回 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();
    }
}

我需要进行更多测试,但到目前为止一切顺利。

I'm building a RESTful API and want to provide developers with the option to choose which fields to return in the JSON response. This blog post shows examples of how several API's (Google, Facebook, LinkedIn) allow developers to customize the response. This is referred to as partial response.

An example might look like this:

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

In the example above the API should return the userId, fullName and title fields for User "123".

I'm looking for ideas of how to implement this in my RESTful web service. I'm currently using CXF (edit: and Jackson) but willing to try another JAX-RS implementation.

Here's what I currently have. It returns a full User object. How can I return only the fields the API caller wants at runtime based on the "fields" paramaeter? I don't want to make the other fields Null. I simply don't want to return them.

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

UPDATE:

I followed unludo's link which then linked to this: http://wiki.fasterxml.com/JacksonFeatureJsonFilter

With that info I added @JsonFilter("myFilter") to my domain class. Then I modified my RESTful service method to return String instead of User as follows:

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

I need to do more testing but so far so good.

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

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

发布评论

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

评论(3

风启觞 2025-01-13 10:48:38

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

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

我对您找到的解决方案感兴趣。

If you use Jackson (a great JSON lib - kind of the standard for Java I believe), you may use the @View annotation to filter what you want in the resulting object.

I understand that you want something dynamic so it's a bit more complicated. You will find what you are looking for here: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html (look at 6. Fully dynamic filtering: @JsonFilter).

I would be interested in the solution you will find.

此生挚爱伱 2025-01-13 10:48:38

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

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

下面的示例展示了如何注册 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"}

Creating an ObjectMapper instance inside the resource method for every request can have significant performance overhead. According to the Jackson performance best practices object mappers are expensive to create.

Instead you can customize the JAX-RS provider's Jackson object writer inside the resource method using the Jackson 2.3 ObjectWriterModifier/ObjectReaderModifier feature.

Here is an example shows how to register an ObjectWriterModifier thread local object that changes the set of the filters applied for the JAX-RS Jackson provider being used inside a resource method. Note that I have not tested the code against an JAX-RS implementation.

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

}

Output:

{"field1":"a"}
岁吢 2025-01-13 10:48:38

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

URL

people/1234?select=streetAddress,地区

RETURN

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

The Library jersey-entity-filtering Can do that :

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

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

Exemple :

My Object

public class Address {

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

URL

people/1234?select=streetAddress,region

RETURN

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

Add to Maven

<dependency>
    <groupId>org.glassfish.jersey.ext</groupId>
    <artifactId>jersey-entity-filtering</artifactId>
    <version>2.22.2</version>
</dependency>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文