web-dev-qa-db-ja.com

JAXBオブジェクトのリストのシリアル化をJSONにカスタマイズするにはどうすればよいですか?

Jerseyを使用して、サーバーコンポーネントのREST Webサービスを作成しています。

リストでシリアル化するJAXB注釈付きオブジェクトは次のようになります。

_@XmlRootElement(name = "distribution")
@XmlType(name = "tDistribution", propOrder = {
    "id", "name"
})
public class XMLDistribution {
    private String id;
    private String name;
    // no-args constructor, getters, setters, etc
}
_

私はRESTリソースを持ち、次のような1つのディストリビューションを取得します。

_@Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
    @GET
    @Produces("application/json")
    public XMLDistribution retrieve(@PathParam("id") String id) {
        return retrieveDistribution(Long.parseLong(id));
    }
    // business logic (retrieveDistribution(long))
}
_

また、すべてのディストリビューションのリストを取得するためのRESTリソースがあります。これは次のようになります。

_@Path("/distributions")
public class RESTDistributions {
    @GET
    @Produces("application/json")
    public List<XMLDistribution> retrieveAll() {
        return retrieveDistributions();
    }
    // business logic (retrieveDistributions())
}
_

ContextResolverを使用してJAXBシリアル化をカスタマイズします。現在、次のように構成されています。

_@Provider
@Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
    private JAXBContext context;
    public JAXBJSONContextResolver() throws Exception {
        JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
        b.nonStrings("id");
        b.rootUnwrapping(true);
        b.arrays("distribution");
        context = new JSONJAXBContext(b.build(), XMLDistribution.class);
    }
    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return context;
    }
}
_

両方のRESTリソースはコンテキストリゾルバと同様に機能します。これは最初の出力の例です:

_// path: /distribution/1
{"id":1,"name":"Example Distribution"}
_

それはまさに私が欲しいものです。これはリストの出力例です:

_// path: /distributions
{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}
_

これは私が望むものではありません。

なぜdistributionタグで囲まれているのかわかりません。コンテキストリゾルバで.rootUnwrapping(true)で削除したかったのですが、明らかにそれは別の囲んでいるタグを削除するだけです。これは.rootUnwrapping(false)の出力です:

_// path: /distribution/1
{"distribution":{"id":1,"name":"Example Distribution"}} // not ok
// path: /distributions
{"xMLDistributions":{"distribution":[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]}}
_

また、要素が1つだけであっても、常にJSON配列を取得するように.arrays("distribution")を構成する必要がありました。

理想的には、出力としてこれが欲しいです:

_// path: /distribution/1
{"id":1,"name":"Example Distribution"} // currently works
// path: /distributions
[{"id":1,"name":"Sample Distribution 1"},{"id":2,"name":"Sample Distribution 2"}]
_

_List<XMLDistribution>_、XMLDistributionList(リストを囲むラッパー)、_XMLDistribution[]_を返そうとしましたが、ディストリビューションの単純なJSON配列を取得する方法が見つかりませんでした必要な形式。

JSONConfiguration.natural()JSONConfiguration.mappedJettison()などによって返される他の表記法も試してみましたが、必要なものに似たものを得ることができませんでした。

これを行うためにJAXBを設定することが可能であるかどうかを知っていますか?

54
Alpha Hydrae

私は解決策を見つけました。JAXBJSONシリアライザーを、Jacksonのような動作の良いJSONシリアライザーに置き換えてください。簡単な方法はjackson-jaxrsを使用することです。これは既にあなたのためにそれを行っています。クラスはJacksonJsonProviderです。必要なのは、Jersey(または別のJAX-RS実装)がスキャンするようにプロジェクトのweb.xmlを編集することだけです。追加する必要があるものは次のとおりです。

<init-param>
  <param-name>com.Sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>

そして、それだけです。 JacksonはJSONのシリアル化に使用され、リストと配列に期待される方法で動作します。

より長い方法は、「application/json」を生成するために登録された独自のカスタムMessageBodyWriterを記述することです。以下に例を示します。

 @ Provider 
 @ Produces( "application/json")
 publicクラスJsonMessageBodyWriterはMessageBodyWriterを実装します{
 @Override 
 public long getSize(Object obj 、クラスタイプ、Type genericType、
 Annotation []アノテーション、MediaType mediaType){
 return -1; 
} 
 
 @Override 
 public boolean isWriteable(Class type、Type genericType、
 Annotation annotations []、MediaType mediaType){
 return true; 
} 
 
 @Override 
 public void writeTo(Object target、Class type、Type genericType、
 Annotation [] annotations、MediaType mediaType、
 MultivaluedMap httpHeaders、OutputStream outputStream)
 throws IOException {
 new ObjectMapper()。writeValue(outputStream、target); 
} 
} 

上記の既製のソリューションのように、web.xmlにパッケージが含まれていることを確認する必要があります。

いずれにせよ:出来上がり!適切に形成されたJSONが表示されます。

ここからジャクソンをダウンロードできます: http://jackson.codehaus.org/

102
Jonathan

ジョンハタンの答えはすばらしく、私にとって非常に役に立ちました。

ただのアップグレード:

jacksonのバージョン2.x(バージョン2.1など)を使用する場合、クラスはcom.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProviderであるため、web.xmlは次のようになります。

<init-param>
  <param-name>com.Sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>
13
Gabriele