web-dev-qa-db-ja.com

Javaを使用して部分的なJSON応答を返す方法は?

RESTful APIを構築していますが、開発者にJSON応答で返すフィールドを選択するオプションを提供したいと考えています。 このブログ投稿 は、いくつかのAPI(Google、Facebook、LinkedIn)を使用して開発者が応答をカスタマイズする方法の例を示しています。これは、部分応答と呼ばれます。

例は次のようになります。

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

上記の例では、APIはユーザー「123」のuserId、fullName、およびtitleフィールドを返します。

これをRESTful Webサービスに実装する方法のアイデアを探しています。現在、CXF(編集:and Jackson)を使用していますが、別のJAX-RS実装を試してみます。

ここに私が現在持っているものがあります。完全なUserオブジェクトを返します。 「フィールド」パラメータに基づいて、実行時に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;
}
_

UPDATE:

これにリンクした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();
    }
}
_

さらにテストを行う必要がありますが、これまでのところこれで十分です。

41
Justin

ジャクソン(素晴らしいJSONライブラリ-Java私は信じる)の一種)を使用する場合、@Viewアノテーションを使用して、結果のオブジェクトで必要なものをフィルタリングできます。

動的なものが欲しいので、もう少し複雑になります。あなたがここで探しているものを見つけるでしょう: http://www.cowtowncoder.com/blog/archives/2011/02/entry_443.html ( 6.完全に動的なフィルタリング:@JsonFilter)をご覧ください。

私はあなたが見つける解決策に興味があります。

18
unludo

リクエストごとにリソースメソッド内にObjectMapperインスタンスを作成すると、パフォーマンスのオーバーヘッドが大きくなる可能性があります。 Jacksonのパフォーマンスのベストプラクティス によると、オブジェクトマッパーの作成には費用がかかります。

代わりに Jackson 2.3 ObjectWriterModifier/ObjectReaderModifier機能 を使用して、リソースメソッド内でJAX-RSプロバイダーのJacksonオブジェクトライターをカスタマイズできます。

リソースメソッド内で使用されているJAX-RS Jacksonプロバイダーに適用されるフィルターのセットを変更するObjectWriterModifierスレッドローカルオブジェクトを登録する方法の例を次に示します。 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"}
5
Alexey Gavrilov

ライブラリ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、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>
4
Hydral