1

Java と Jersey を使用して RESTful Web サービスを作成しています。サービスは XML または JSON 入力を受け入れます。Jackson は JSON デシリアライザーとして使用され、Jersey 構成に統合されています。

エンドポイントの 1 つは、URL への POST 要求です。このコンテンツは、いくつかの異なる Java クラスのいずれかであり、共通の基本クラスがあります。これらのクラス (XML 注釈付き) は次のとおりです。

@XmlRootElement(name = "action")
@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({ FirstAction.class, SecondAction.class, ThirdAction.class })
public abstract class BaseAction {
}

@XmlRootElement(name = "first-action")
@XmlAccessorType(XmlAccessType.NONE)
public class FirstAction extends BaseAction implements Serializable {
}

// Likewise for SecondAction, ThirdAction

私のリソースでは、次のようなメソッドを宣言できます。

@POST
@Path("/{id}/action")
public Response invokeAction(@PathParam("id") String id, BaseAction action) {...}

次に、次のような XML フラグメントを POST できます。<firstAction/>メソッドはインスタンスで呼び出されFirstActionます。ここまでは順調ですね。

私が苦労しているのは、JSON デシリアライゼーションを XML デシリアライゼーションと同じくらいシームレスに機能させることです。XML デシリアライゼーションを適切に機能させるためにアノテーションが重要である場合@XmlSeeAlso、JSON に相当するのは@JsonSubTypes. そこで、次のようにクラスに注釈を付けました。

// XML annotations removed for brevity, but they are present as in the previous code snippet
@JsonSubTypes({ @JsonSubTypes.Type(name = "first-action", value = FirstAction.class),
    @JsonSubTypes.Type(name = "second-action", value = SecondAction.class),
    @JsonSubTypes.Type(name = "third-action", value = ThirdAction.class) })
public abstract class BaseAction {
}

@JsonRootName("first-action")
public class FirstAction extends BaseAction implements Serializable {
}

// Likewise for SecondAction, ThirdAction

次に、テスト入力をフィードします{ "first-action": null }が、取得できるのは次のとおりです。

「org.codehaus.jackson.map.JsonMappingException: ルート名 'first-action' が、タイプ [単純なタイプ、クラス com.alu.openstack.domain.compute.server.actions.BaseAction] の予期される ('action') と一致しません"

残念ながら、私は他の誰かの API との互換性を維持しようとしているので、サンプル入力を変更することはできません{ "first-action": null }。動作して、クラス FirstAction のオブジェクトをメソッドに配信する必要があります。(アクションにはフィールドがありません。そのため、null が問題になることはありません。重要なのはクラスの型です)。

JSON デシリアライゼーションを XML デシリアライゼーションと同じように動作させる正しい方法は何ですか?

4

3 に答える 3

2

Jackson を使用している場合は、@JsonTypeInfoandを探しています@Type。詳しくはこちらをご覧ください

于 2012-08-16T11:01:35.103 に答える
1

JSON は XML のようには機能しないため、ソリューションは同じではありません。

使用する必要があるのは(他の回答が言ったように)@JsonTypeInfo、. これは、タイプ識別子の包含と使用をトリガーするだけです。もしそうなら、'@JsonSubTypes` はデシリアライズに役立ちます。

このインジケーターを使用する必要がある理由は単純です。逆シリアル化する代替型が複数ある場合は、区別する必要があるからです。

また、これはプロパティである必要はないことに注意してください。ほとんどのユーザーは「As.PROPERTY」を含めることを選択しますが、(IMO) 最善の方法ではありません。"WRAPPER_OBJECT" は、XML と似たような追加の中間 JSON プロパティを追加するため、探しているものかもしれません。

于 2012-08-16T17:14:49.963 に答える
0

の使用を調査しました@JsonTypeInfoが、入力形式を変更できなかったため、問題が発生しました。パーサーは絶対に入力を処理できなければなりません{ "first-action":null }でした。@typeこれにより、 or@classプロパティを追加する可能性が排除されました。nullラッパー オブジェクトの使用は機能していた可能性がありますが、ペイロードが詰まっていました。

重要な点は、UNWRAP_ROOT_PROPERTY 構成オプションを使用していたことです。ジャクソンは物件を見つけることを絶対に主張してactionいたので、私はそれ以外のことを検討することができませんでした. そのため、特定のドメイン オブジェクトに対して UNWRAP_ROOT_PROPERTY を選択的に無効にする必要がありました。これにより、Jackson が代替の構文解析に対してオープンになるようになりました。プロジェクトの ContextResolver.getContext(...) 実装を変更して@JsonRootNameアノテーションをチェックしました。これはラッピングが有効な場合にのみ意味があるため、このアノテーションの存在を使用して、ルート プロパティ ラッピングで構成されたオブジェクト マッパーを返すかどうかを決定しました。 、またはオフ。

この段階では、上記@JsonTypeInfo(include=JsonTypeInfo.As.WRAPPER_OBJECT, ...)のペイロードの問題を除いて、を使用できた可能性がありますnull(これは、子オブジェクトにプロパティがないことを示すために使用されます - 私が作業していた仕様が代わりに空のオブジェクト {} を指定した場合)その場合は問題ありません)。先に進むには、カスタム型リゾルバーが必要でした。

org.codehaus.jackson.map.TypeDeserializerインスタンスをデシリアライズするために Jackson が呼び出されるたびに、BaseActionこのカスタム デシリアライザーを呼び出すという目的で、を拡張した新しいクラスを作成しました。デシリアライザーにはサブタイプ配列が与えられ、BaseActionfirst-actionではsecond-action、などを FirstAction.class などにマップします。デシリアライザーはフィールド名の入力ストリームを読み取り、その名前をクラスに一致させます。次のトークンがオブジェクトの場合は、そのクラスの適切なデシリアライザーを見つけて委譲します。または、null の場合は、引数なしのコンストラクターを見つけて呼び出し、オブジェクトを取得します。

この前のクラスのインスタンスを構築できる org.codehaus.jackson.map.jsontype.TypeResolverBuilder を実装するクラスが必要であり、次に TypeResolverBuilder がクラスの@JsonTypeResolver注釈として与えられBaseActionます。

于 2012-08-23T09:45:38.050 に答える