Menu Close

37.2. Java での置換グループ

概要

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

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

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

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

プロパティー説明

substitutionHeadNamespace

先頭要素が定義される namespace を指定します。

substitutionHeadName

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

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

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

例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);
    }

}

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

置換グループの先頭要素が操作のメッセージの 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 アノテーションのオブジェクトファクトリーをリストします。namespace のオブジェクトファクトリーを一覧表示すると、その namespace の生成されたクラスすべてにアクセスできます。

複合型での置換グループ

置換グループの先頭要素を複合型の要素として使用する場合、コードジェネレーターは要素を 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();
}