37.2. Java の置換グループ

概要

JAXB 仕様で指定されるように、Apache CXF は、Java のネイティブクラス階層を使用し、これに JAXBElement クラスのワイルドカード定義に対するサポートを組み合わせて、置換グループをサポートします。置換グループのメンバーはすべて共通のベースタイプを共有する必要があるため、要素のタイプをサポートするために生成されたクラスも共通のベースタイプを共有します。さらに、Apache CXF は先頭要素のインスタンスを JAXBElement<? extends T> プロパティーにマッピングします。

生成されたオブジェクトファクトリーメソッド

置換グループを含むパッケージをサポートするために生成されたオブジェクトファクトリーには、置換グループ内の各要素のメソッドがあります。表37.1「JAXB 要素を宣言するためのプロパティーは置換グループのメンバーです」で説明されているように、先頭要素を除く置換グループの各メンバーについて、オブジェクトファクトリーメソッドに付けられる @XmlElementDecl アノテーションには 2 つの追加プロパティーが含まれます。

表37.1 JAXB 要素を宣言するためのプロパティーは置換グループのメンバーです

プロパティー説明

substitutionHeadNamespace

ヘッド要素が定義されている名前空間を指定します。

substitutionHeadName

先頭要素の name 属性の値を指定します。

置換グループの @XmlElementDecl の先頭要素のオブジェクトファクトリーメソッドには、デフォルトの namespace プロパティーとデフォルトの name プロパティーのみが含まれます。

要素のインスタンス化メソッドに加えて、オブジェクトファクトリーには head 要素を表すオブジェクトをインスタンス化するためのメソッドが含まれています。置換グループのメンバーがすべて複合型である場合、オブジェクトファクトリーには、使用される各複合型のインスタンスをインスタンス化するためのメソッドも含まれています。

例37.5「置換グループのオブジェクトファクトリーメソッド」 は、例37.2「複雑なタイプの置換グループ」 で定義された置換グループのオブジェクトファクトリーメソッドを示します。

例37.5 置換グループのオブジェクトファクトリーメソッド

public class ObjectFactory {

    private final static QName _Widget_QNAME = new QName(...);
    private final static QName _PlasticWidget_QNAME = new QName(...);
    private final static QName _WoodWidget_QNAME = new QName(...);

    public ObjectFactory() {
    }

    public WidgetType createWidgetType() {
        return new WidgetType();
    }

    public PlasticWidgetType createPlasticWidgetType() {
        return new PlasticWidgetType();
    }

    public WoodWidgetType createWoodWidgetType() {
        return new WoodWidgetType();
    }

    @XmlElementDecl(namespace="...", name = "widget")
    public JAXBElement<WidgetType> createWidget(WidgetType value) {
        return new JAXBElement<WidgetType>(_Widget_QNAME, WidgetType.class, null, value);
    }

    @XmlElementDecl(namespace = "...", name = "plasticWidget", substitutionHeadNamespace = "...", substitutionHeadName = "widget")
    public JAXBElement<PlasticWidgetType> createPlasticWidget(PlasticWidgetType value) {
        return new JAXBElement<PlasticWidgetType>(_PlasticWidget_QNAME, PlasticWidgetType.class, null, value);
    }

    @XmlElementDecl(namespace = "...", name = "woodWidget", substitutionHeadNamespace = "...", substitutionHeadName = "widget")
    public JAXBElement<WoodWidgetType> createWoodWidget(WoodWidgetType value) {
        return new JAXBElement<WoodWidgetType>(_WoodWidget_QNAME, WoodWidgetType.class, null, value);
    }

}

インターフェイスの置換グループ

置換グループの head 要素が操作のメッセージの 1 つでメッセージ部分として使用される場合、結果のメソッドパラメーターは、その要素をサポートするために生成されたクラスのオブジェクトになります。これは必ずしも JAXBElement<? extends T> クラスのインスタンスであるとは限りません。ランタイムは、Java のネイティブ型階層に依存して型置換をサポートし、Java はサポートされていない型を使用しようとする試みをすべてキャッチします。

ランタイムが要素の置換のサポートに必要なすべてのクラスを認識するために、SEI に @XmlSeeAlso アノテーションが付けられます。このアノテーションは、マーシャリングのためにランタイムに必要なクラスのリストを指定します。@XmlSeeAlso アノテーションの使用に関する詳細は、「ランタイムマーシャラーへのクラスの追加」 を参照してください。

例37.7「置換グループを使用して生成されたインターフェイス」 は、例37.6「置換グループを使用した WSDL インターフェイス」 に示されているインターフェイスに対して生成された SEI を示しています。インターフェイスは、例37.2「複雑なタイプの置換グループ」 で定義された置換グループを使用します。

例37.6 置換グループを使用した WSDL インターフェイス

<message name="widgetMessage">
    <part name="widgetPart" element="xsd1:widget" />
  </message>
  <message name="numWidgets">
    <part name="numInventory" type="xsd:int" />
  </message>
  <message name="badSize">
    <part name="numInventory" type="xsd:int" />
  </message>
  <portType name="orderWidgets">
    <operation name="placeWidgetOrder">
      <input message="tns:widgetOrder" name="order" />
      <output message="tns:widgetOrderBill" name="bill" />
      <fault message="tns:badSize" name="sizeFault" />
    </operation>
    <operation name="checkWidgets">
      <input message="tns:widgetMessage" name="request" />
      <output message="tns:numWidgets" name="response" />
    </operation>
  </portType>

例37.7 置換グループを使用して生成されたインターフェイス

@WebService(targetNamespace = "...", name = "orderWidgets")
@XmlSeeAlso({com.widgetvendor.types.widgettypes.ObjectFactory.class})
public interface OrderWidgets {

    @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
    @WebResult(name = "numInventory", targetNamespace = "", partName = "numInventory")
    @WebMethod
    public int checkWidgets(
        @WebParam(partName = "widgetPart", name = "widget", targetNamespace = "...")
        com.widgetvendor.types.widgettypes.WidgetType widgetPart
    );
}

例37.7「置換グループを使用して生成されたインターフェイス」 に示される SEI は、@XmlSeeAlso アノテーションにオブジェクトファクトリーを一覧表示します。名前空間のオブジェクトファクトリーを一覧表示すると、その名前空間に対して生成されたすべてのクラスにアクセスできます。

複雑なタイプの置換グループ

置換グループの先頭要素を複合型の要素として使用する場合、コードジェネレーターは要素を JAXBElement<? extends T> プロパティーにマッピングします。置換グループをサポートするために生成された生成されたクラスのインスタンスを含むプロパティーにはマップされません。

たとえば、例37.8「置換グループを使用した複合型」 で定義された複合型結果は、例37.9「置換グループを使用した複合型の Java クラス」 に示す Java クラスになります。複合型は、例37.2「複雑なタイプの置換グループ」 で定義された置換グループを使用します。

例37.8 置換グループを使用した複合型

<complexType name="widgetOrderInfo">
  <sequence>
    <element name="amount" type="xsd:int"/>
    <element ref="xsd1:widget"/>
  </sequence>
</complexType>

例37.9 置換グループを使用した複合型の Java クラス

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "widgetOrderInfo", propOrder = {"amount","widget",})
public class WidgetOrderInfo {

    protected int amount;
    @XmlElementRef(name = "widget", namespace = "...", type = JAXBElement.class) protected JAXBElement<? extends WidgetType> widget;
    public int getAmount() {
        return amount;
    }

    public void setAmount(int value) {
        this.amount = value;
    }

    public JAXBElement<? extends WidgetType> getWidget() { return widget; }

    public void setWidget(JAXBElement<? extends WidgetType> value) { this.widget = ((JAXBElement<? extends WidgetType> ) value); }

}

置換グループプロパティーの設定

置換グループの操作方法は、コードジェネレーターがグループを単純な Java クラスにマッピングしたか、または JAXBElement<? extends T> クラスにマッピングしたかによって異なります。要素が生成された値クラスのオブジェクトに単純にマップされる場合、型階層の一部である他の Java オブジェクトを操作するのと同じ方法でオブジェクトを操作します。親クラスの代わりに任意のサブクラスを使用できます。オブジェクトを調べて正確なクラスを判別し、適切にキャストできます。

JAXB 仕様では、生成されたクラスのオブジェクトをインスタンス化するためにオブジェクトファクトリーメソッドを使用することを推奨しています。

コードジェネレーターが置換グループのインスタンスを保持するのに JAXBElement<? extends T> オブジェクトを作成する場合、JAXBElement<? extends T> オブジェクトに要素の値をラップする必要があります。これを行うための最良の方法は、オブジェクトファクトリーによって提供される要素作成メソッドを使用することです。それらは、その値に基づいて要素を作成するための簡単な手段を提供します。

例37.10「代替グループのメンバーの設定」 は、置換グループのインスタンスを設定するためのコードを示しています。

例37.10 代替グループのメンバーの設定

ObjectFactory of = new ObjectFactory();
PlasticWidgetType pWidget = of.createPlasticWidgetType();
pWidget.setShape = "round';
pWidget.setColor = "green";
pWidget.setMoldProcess = "injection";

JAXBElement<PlasticWidgetType> widget = of.createPlasticWidget(pWidget);

WidgetOrderInfo order = of.createWidgetOrderInfo();
order.setWidget(widget);

例37.10「代替グループのメンバーの設定」 のコードは、以下を行います。

オブジェクトファクトリーをインスタンス化します。

PlasticWidgetType オブジェクトをインスタンス化します。

JAXBElement<PlasticWidgetType> オブジェクトをインスタンス化して、プラスチックウィジェット要素を保持します。

WidgetOrderInfo オブジェクトをインスタンス化します。

WidgetOrderInfo オブジェクトの widget をプラスチックウィジェット要素を保持する JAXBElement オブジェクトに設定します。

置換グループプロパティーの値を取得する

オブジェクトファクトリーの手法は、JAXBElement<? extends T> オブジェクトから要素の値を抽出する際には役立ちません。JAXBElement<? extends T> オブジェクトの getValue() メソッドを使用する必要があります。以下のオプションは、getValue() メソッドによって返されるオブジェクトの型を決定します。

  • 使用できるすべてのクラスの isInstance() メソッドを使用して、要素の値オブジェクトのクラスを決定します。
  • JAXBElement<? extends T> オブジェクトの getName() メソッドを使用して、要素の名前を決定します。

    getName() メソッドは QName を返します。要素のローカル名を使用して、値オブジェクトの適切なクラスを決定できます。

  • JAXBElement<? extends T> オブジェクトの getDeclaredType() メソッドを使用して、値オブジェクトのクラスを決定します。

    getDeclaredType() メソッドは、要素の値オブジェクトの Class オブジェクトを返します。

    警告

    値オブジェクトの実際のクラスに関係なく、getDeclaredType() メソッドが先頭要素のベースクラスを返す可能性があります。

例37.11「代替グループのメンバーの値を得る」 は、置換グループから値を取得するコードを示しています。要素の値オブジェクトに適切なクラスを決定するために、このサンプルでは要素の getName() メソッドを使用します。

例37.11 代替グループのメンバーの値を得る

String elementName = order.getWidget().getName().getLocalPart();
if (elementName.equals("woodWidget")
{
  WoodWidgetType widget=order.getWidget().getValue();
}
else if (elementName.equals("plasticWidget")
{
  PlasticWidgetType widget=order.getWidget().getValue();
}
else
{
  WidgetType widget=order.getWidget().getValue();
}