Jersey 2.4を使用して、JSONオブジェクトを提供するシンプルなRESTインターフェースを作成しています。私の問題は、fasterxml Jacksonアノテーションを使用して出力を制御しようとしているため、これが私はBeanクラスに注釈を入れましたが、それらは無視されます。
ObjectMapperを明示的に作成し、これを使用してJava Beanを文字列化する)と、Jacksonアノテーションを尊重する必要な出力が得られます。ただし、この手順を実行して、リソースクラスがBeanを返すだけで、Jerseyフレームワークがそれを文字列化するようにします。
Jersey 2.2およびJackson 2.1を使用したカスタムObjectMapper からの回答を使用してこれを解決しようとしましたが、これは私には機能しないようです。 ContextResolverは作成されていますが、呼び出されることはありません。
私はこの一見単純な問題を解決するために何時間も費やしました。以下に示すように、これを非常に単純なテストケースに取り除きました。私はこれを解決するために少しでも助けていただければ幸いです。
リソースJavaクラス:
@Path("resource")
public class MainResource {
public static class Foobar {
@JsonIgnore
private String foo = "foo";
private String baa = "baa";
private Map<String, List<? extends Number>> map = new HashMap<>();
public Foobar() {
map.put("even", Arrays.asList(new Integer[] { 2, 4, 6, 8, 10 }));
map.put("odd", Arrays.asList(new Integer[] { 1, 3, 5, 7, 9 }));
map.put("float", Arrays.asList(new Float[] { 1.1F, 2.2F, 3.3F }));
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBaa() {
return baa;
}
public void setBaa(String baa) {
this.baa = baa;
}
@JsonAnyGetter
public Map<String, List<? extends Number>> getMap() {
return map;
}
public void setMap(Map<String, List<? extends Number>> map) {
this.map = map;
}
}
private ObjectMapper om = new ObjectMapper();
@GET
@Path("get-object")
@Produces(MediaType.APPLICATION_JSON)
public Foobar getObject() {
// In this method, I simply return the bean object but the WRONG JSON syntax is generated.
return new Foobar();
}
@GET
@Path("get-string")
@Produces(MediaType.APPLICATION_JSON)
public String getString() throws JsonProcessingException {
// This method returns the RIGHT JSON syntax but I don't want to have to explicitly use the ObjectMapper.
Foobar foobar = new Foobar();
return om.writeValueAsString(foobar);
}
}
web.xml:
<web-app version="3.0" xmlns="http://Java.Sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee http://Java.Sun.com/xml/ns/javaee/web-app_3_0.xsd">
<module-name>sample</module-name>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>ie.cit.nimbus.sample</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
POMの依存関係:
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.4.1</version>
</dependency>
</dependencies>
[〜#〜] edit [〜#〜]:以下の古いアプローチを使用しないでくださいバグが発生するため(少なくともAndroidデバイスでは、詳細についてはEDIT2を参照)。私のテストの時点では、Jersey v2.6は@Provide
の問題を解決しているようですが、このアプローチは機能しませんでした。私はそれをこの単純なプロバイダーで動作させることができました:
@Provider
public class JerseyMapperProvider implements ContextResolver<ObjectMapper> {
private static ObjectMapper apiMapper = ObjectMapperManager.createMapperForApi();
@Override
public ObjectMapper getContext(Class<?> type)
{
return apiMapper;
}
}
下から私のハックを使わないでください。
古いアプローチ
使用する
@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>
私(Jersey 2.4およびJackson 2.3)で機能していませんでした。多分これは、ContextResolver
をJacksonJsonProvider.Java
(2.3rc1)に登録する必要があるコード内のjacksonプロバイダーで報告されたバグが原因です。
@Override
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType)
{
if (_providers != null) {
ContextResolver<ObjectMapper> resolver = _providers.getContextResolver(ObjectMapper.class, mediaType);
/* Above should work as is, but due to this bug
* [https://jersey.dev.Java.net/issues/show_bug.cgi?id=288]
* in Jersey, it doesn't. But this works until resolution of
* the issue:
*/
if (resolver == null) {
resolver = _providers.getContextResolver(ObjectMapper.class, null);
}
if (resolver != null) {
return resolver.getContext(type);
}
}
return null;
}
しかし、少なくとも https://jersey.dev.Java.net/issues/show_bug.cgi?id=288 にはアクセスできないため、このバグの内容がわかりません。
しかし、私は回避策(もしそうならハック)を見つけました。適切な注釈でJacksonJsonProvider
を拡張し、ObjectMapper
を次のように返します。
@Provider
@Consumes(MediaType.APPLICATION_JSON) // NOTE: required to support "non-standard" JSON variants
@Produces(MediaType.APPLICATION_JSON)
public class JacksonHackProvider extends JacksonJsonProvider {
@Override
protected ObjectMapper _locateMapperViaProvider(Class<?> type, MediaType mediaType) {
return new MyCustomObjectMapper();
}
}
それ自体が登録されるため、何もする必要はありません(ログで確認してください。jsonレストサービスに初めてアクセスしたときに登録されます)。これはエレガントではなく、今は私のために働いていますが、あきらめました。
編集:注意して使用してください-このハックに関連する可能性のあるバグが発生しています:Android volleyはリクエスト本文を含むPOST/PUTリクエストを送信できず、常にフレームワークから400を取得します。調査結果を報告します。
EDIT2:このハックは、実際にジェネリック400の原因であり、AndroidアプリがボレーとOKHTTP
クライアントを使用してPOSTまたはPUTリクエストを実行しようとしたため、これを使用しないでください-私のテストジャージー2.6はこれを修正するようで、@Provide
アプローチを使用できます
残念ながら、誰もがこれを必要以上に難しくしています。知恵のあるジャージーチームは、Jackson 1.9を統合することを決定しました。
しかし、それは私にとってはとても簡単でした。これを行うだけです:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.3.0</version>
</dependency>
これを取り除く:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.4.1</version>
</dependency>
次に、web.xmlで次の行を変更します。
<param-value>ie.cit.nimbus.sample</param-value>
することが:
<param-value>ie.cit.nimbus.sample,com.fasterxml.jackson.jaxrs.json</param-value>
それでうまくいくはずです。
Jersey 2.13
を使用すると、@Provider
が同じObjectMapper
を使用するように強制でき、ObjectMapper
を1つだけ作成します。
package com.example.api.environment.configs;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.joda.JodaModule;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {
private final ObjectMapper objectMapper;
public JacksonConfig() {
objectMapper = new ObjectMapper()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.registerModule(new JodaModule());
};
@Override
public ObjectMapper getContext(Class<?> type) {
return objectMapper;
}
}
これをObjectMapper
の@Autowire
に使用して、オンザフライでjson-schema
ドキュメントを生成します。
jacksonをJax-rsJersey実装と統合する方法はたくさんあります。
Mkyongチュートリアルを見てみると、 http://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/ も渡す必要があるようです"POJOMappingFeature"-> web.xmlのinit paramsでtrue。これはJersey 1.8で機能すると思います
代わりにJerseyの公式ドキュメントを確認すると、次のようになります。 https://jersey.Java.net/nonav/documentation/latest/user-guide.html#json.jackson 実装する必要があるようですJax-rsプロバイダーとそのプロバイダーをアプリケーションリソースに追加する
@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>
これらは、これを行う方法の例を提供します https://github.com/jersey/jersey/blob/2.4.1/examples/json-jackson/src/main/Java/org/glassfish/jersey/ examples/jackson/MyObjectMapperProvider.Java
私はこの方法を使用して問題を解決し、JacksonアノテーションはJacksonプロバイダーによって正しくスキャンされました。
オフトピック私はあなたのBeanでこの構文を使用してマップを初期化することをお勧めします:
import static Java.util.Arrays.asList;
public static class Foobar {
@JsonIgnore
private String foo = "foo";
private String baa = "baa";
private Map<String, List<? extends Number>> map = new HashMap<>(){{
put("even", asList(2, 4, 6, 8, 10));
put("odd", asList(1, 3, 5, 7, 9));
put("float", asList(1.1F, 2.2F, 3.3F));
}};
public Foobar() {
}
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getBaa() {
return baa;
}
public void setBaa(String baa) {
this.baa = baa;
}
@JsonAnyGetter
public Map<String, List<? extends Number>> getMap() {
return map;
}
public void setMap(Map<String, List<? extends Number>> map) {
this.map = map;
}
}