web-dev-qa-db-ja.com

REST SpringおよびJacksonの完全なデータバインディング

私はSpring MVCを使用してJSON POSTリクエストを処理しています。カバーの下では、Jackson JSONプロセッサ上に構築され、mvc:annotation-drivenを使用すると有効になるMappingJacksonHttpMessageConverterを使用しています。

私のサービスの1つがアクションのリストを受け取ります。

@RequestMapping(value="/executeActions", method=RequestMethod.POST)
    public @ResponseBody String executeActions(@RequestBody List<ActionImpl> actions) {
        logger.info("executeActions");
        return "ACK";
    }

ジャクソンがrequestBodyをJava.util.LinkedHashMapアイテムのリスト(単純なデータバインディング)にマップすることを発見しました。代わりに、リクエストを型付きオブジェクトのリスト(この場合は「ActionImpl」)にバインドしたいと思います。

ジャクソンのObjectMapperを直接使用する場合、これは簡単に実行できます。

List<ActionImpl> result = mapper.readValue(src, new TypeReference<List<ActionImpl>>() { }); 

しかし、Spring MVCとMappingJacksonHttpMessageConverterを使用するときにこれを達成するための最良の方法は何だろうと思っていました。ヒントはありますか?

ありがとう

40
Javier Ferrero

問題の原因は型の消去にあると思います。つまり、ジェネリックパラメーターの型を渡す代わりに、actions.getClass()のみが渡される可能性があります。これにより、List <?>と同等の型が得られます。

これが当てはまる場合、1つの可能性は、次のような中間サブクラスを使用することです。

public class ActionImplList extends ArrayList<ActionImpl> { }

これは、クラスのみが渡された場合でもタイプ情報を保持するためです。それで:

public @ResponseBody String executeActions(@RequestBody ActionImplList actions)

トリックを行います。最適ではありませんが、機能するはずです。

Spring MVCの知識が豊富な人が、パラメーターの型が渡されない理由(おそらくバグか?)を明らかにできることを願っていますが、少なくとも回避策があります。

28
StaxMan

コレクションの代わりに配列を@RequestBodyとして使用することで、型消去の問題を回避できることもわかりました。たとえば、次のコードは機能します。

public @ResponseBody String executeActions(@RequestBody ActionImpl[] actions) { //... }
43
Michiel Verkaik

参考までに、この機能はSpring 3.2で利用可能になる予定です( https://jira.springsource.org/browse/SPR-957 を参照)。

私はそれを現在のM2でテストしましたが、すぐに使用できます(パラメーター化されたタイプを提供するために追加のアノテーションを提供する必要はありません。新しいMessageConverterによって自動的に解決されます)。

9
Frédéric

この質問はもう古いですが、とにかく少し貢献できると思います。

StaxManが指摘したように、これは型消去によるものです。メソッド定義からのリフレクションを介してできる汎用引数を取得できるため、これは間違いなく可能である必要があります。ただし、問題は HttpMessageConverter のAPIです。

T read(Class<? extends T> clazz, HttpInputMessage inputMessage);

ここでは、List.classのみがメソッドに渡されます。したがって、ご覧のとおり、メソッドのパラメーターの型を調べて実際の型を計算するHttpMessageConverterを実装することはできません。

それでも、独自の回避策をコーディングすることは可能です-HttpMessageConverterを使用しないだけです。 Spring MVCを使用すると、独自の WebArgumentResolver を記述できます。これは、標準の解決メソッドの前に起動します。たとえば、ObjectMapperを直接使用して値を解析する独自のカスタムアノテーション(@JsonRequestBody?)を使用できます。メソッドからパラメータタイプを提供できます。

final Type parameterType= method.getParameterTypes()[index];
List<ActionImpl> result = mapper.readValue(src, new TypeReference<Object>>() {
    @Override
    public Type getType() {
        return parameterType;
    }
});

TypeReferenceの使用方法は実際には想定されていませんが、ObjectMapperはより適切なメソッドを提供していません。

2
waxwing

メソッドを次のように宣言してみましたか?

executeActions(@RequestBody TypeReference<List<ActionImpl>> actions)

私は試していませんが、あなたの質問に基づいて、それが私が最初に行うことですする試します。

0
skaffman