ヘッダーをスキップ
Oracle® Fusion Middleware Oracle TopLinkによるJAXBアプリケーションの開発
12c (12.1.3)
E57540-01
  目次へ移動
目次

前
 
次
 

9 動的JAXBの使用

この章の内容は次のとおりです。

9.1 静的エンティティと動的エンティティの理解

EclipseLink JAXBの高レベルな使用方法には、既存のJavaクラスの使用(「静的MOXyの使用」)とEclipseLinkで生成したメモリー内のJavaクラスの使用(「動的MOXyの使用」)の2つがあります。

9.1.1 静的MOXyの使用

EclipseLink JAXBの最も一般的な使用方法は、Java注釈および/またはEclipseLink OXMメタデータによってXMLにマップされた既存のJavaクラスの使用です。これらのクラスは、ユーザーが自分で記述したものか、XJCコンパイラ・ツールでXMLスキーマから生成したものです。

このアプローチを使用して、XMLへの変換およびXMLからの変換において実際のドメイン・オブジェクトを処理します。例9-1に、JAXBで使用できる単純なJavaクラスを示します。

例9-1 Javaクラスのサンプル

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement
public class Customer {
   @XmlAttribute
   private long id;
 
   private String name;
 
   // ...
   // get() and set() methods
   // ...
}
 

注意:

JAXBで静的クラスを使用する場合は、JAXBのデフォルトの動作を利用し、デフォルトとは異なる部分にのみ注釈を付けることができます。たとえば、JavaクラスのすべてのフィールドはデフォルトでXML要素にマップされるため、nameフィールドへの注釈付けは不要です。ただし、XML属性にマップするためにidフィールドが必要なため、そのように注釈付けしています。


例9-2に、静的JAXBを使用してオブジェクトをアンマーシャリング、変更およびマーシャリングする方法を示します。

例9-2 マーシャリングおよびアンマーシャリングの例

JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class, Address.class);
Customer customer = (Customer) jaxbContext.createUnmarshaller().unmarshal(instanceDoc);
 
Address address = new Address();
address.setStreet("1001 Fleet St.");
 
customer.setAddress(address);
 
jaxbContext.createMarshaller().marshal(customer, System.out);
 

9.1.2 動的MOXyの使用

EclipseLinkの動的MOXyでは、JavaクラスパスにJavaクラス・ファイルをコンパイルしなくても、様々なメタデータ・ソースからJAXBContextをブートストラップでき、既存のJAXB APIを使用してデータのマーシャリングおよびアンマーシャリングを行えます。これにより、必要に応じて以前に生成されたJavaソース・コードを更新および再コンパイルしなくてもメタデータを更新できます。

次の場合は、動的MOXyを検討してください。

  • EclipseLinkでXMLスキーマ(XSD)からマッピングを生成する場合。

  • 具体的なJavaドメイン・クラスを扱わない場合。

9.1.2.1 動的エンティティの使用

実際のJavaクラス(Customer.classAddress.classなど)を使用するかわりに、動的MOXyでは単純なget(propertyName)/set(propertyName, propertyValue) APIを使用してデータを操作します。EclipseLinkは、(メモリー内に)各DynamicEntityに関連付けられたDynamicTypeを生成します。


注意:

DynamicTypesはJavaクラスと類似しています。一方、DynamicEntitiesDynamicTypeのインスタンスとみなすことができます。


例9-3に、動的JAXBを使用してオブジェクトをアンマーシャリング、変更およびマーシャリングする方法を示します。

例9-3 マーシャリングおよびアンマーシャリングの例

DynamicJAXBContext dynamicJAXBContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, myClassLoader, null);
DynamicEntity customer = (DynamicEntity) dynamicJAXBContext.createUnmarshaller().unmarshal(instanceDoc);
 
String lastName = customer.get("lastName");
List orders = customer.get("orders");
...
DynamicEntity address = dynamicJAXBContext.newDynamicEntity("mynamespace.Address");
address.set("street", "1001 Fleet St.");
 
customer.set("lastName", lastName + "Jr.");
customer.set("address", address);
 
dynamicJAXBContext.createMarshaller().marshal(customer, System.out);
 

注意:

メタデータのXML名(複合型名、要素名、属性名)は、『Java Architecture for XML Binding (JAXB) 2.2 Specification』(http://jcp.org/en/jsr/detail?id=222)の「Appendix D: Binding XML Names to Java Identifiers」で説明しているアルゴリズムに従ってJava識別子に変換されます。

例9-3では、XML内のlast-nameはJavaオブジェクト用にlastNameに変換されます。


9.2 EclipseLinkランタイムの指定

EclipseLink動的MOXyをJAXB実装として使用するには、jaxb.propertiesファイル内でEclipseLink DynamicJAXBContextFactoryを特定する必要があります。

  1. jaxb.propertiesという名前のテキスト・ファイルを作成し、新しいJAXBContextsの構築に使用されるファクトリとして、DynamicJAXBContextFactoryを次のように指定します。

    javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory

  2. JAXBContextの作成で使用されるコンテキスト・パスにjaxb.propertiesファイルをコピーします。

  3. 標準のJAXBContext.newInstance(String contextPath) APIを使用してDynamicJAXBContextを作成します。

    DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("org.example.mypackage");

アプリケーション・コードを変更する必要がないため、異なるJAXB実装間を容易に切り替えることができます。

9.2.1 DynamicJAXBContextのインスタンス化

JAXBContextで次のメソッドを使用して、DynamicJAXBContextsの新しいインスタンスを作成できます。

public static JAXBContext newInstance(String contextPath) throws JAXBException
public static JAXBContext newInstance(String contextPath, ClassLoader classLoader) throws JAXBException
public static JAXBContext newInstance(String contextPath, ClassLoader classLoader, Map<String,?> properties) throws JAXBException
  • contextPath: jaxb.propertiesファイルの場所。

  • classLoader: アプリケーションの現在のクラス・ローダー。新しいDynamicTypesが生成される前に、クラスの存在を確認するために最初にクラスを検索する際に使用されます。

  • properties: 新しいDynamicJAXBContextの作成に使用されるプロパティのマップ。このマップには、次の2つのキーのいずれか1つが含まれている必要があります。

    • 複数の値を取れるorg.eclipse.persistence.jaxb.JAXBContextFactory.XML_SCHEMA_KEY

      • 次のいずれかで、XMLスキーマ・ファイルを指すもの。

        • java.io.InputStream

        • org.w3c.dom.Node

        • javax.xml.transform.Source

    • 複数の値を取れるorg.eclipse.persistence.jaxb.JAXBContextProperties.OXM_METADATA_SOURCE

      • 次のいずれかで、OXMファイルを指すもの。

        • java.io.File

        • java.io.InputStream

        • java.io.Reader

        • java.net.URL

        • javax.xml.stream.XMLEventReader

        • javax.xml.stream.XMLStreamReader

        • javax.xml.transform.Source

        • org.w3c.dom.Node

        • org.xml.sax.InputSource


        注意:

        これらのオプションの1つを使用する場合、OXMファイルのxml-bindings要素でパッケージ名要素が定義される必要があります。


      • 前述のセットからのオブジェクトのリスト。


        注意:

        このオプションを使用する場合、OXMファイルのxml-bindings要素でパッケージ名要素が定義される必要があります。


      • Map<String, Object>。ここで、Stringはパッケージ名で、Objectは前述の可能な値セットからOXMファイルへのポインタです。

9.3 XMLスキーマ(XSD)からのブートストラップ

EclipseLink MOXyを使用して、既存のXMLスキーマからDynamicJAXBContextを作成できます。EclipseLinkはスキーマを解析し、各複合型に対してDynamicTypesを生成します。これは、DynamicJAXBContextFactoryクラスの使用により実現されます。DynamicJAXBContextは直接インスタンス化できません。ファクトリAPIを介して作成する必要があります。

次のものを使用して、XMLスキーマをDynamicJAXBContextFactoryに渡すことができます。


注意:

EclipseLink MOXyは、SunのXJC (XMLからJavaへのコンパイラ)APIを使用してスキーマをメモリー内表現に解析し、動的タイプとマッピングを生成します。XSDからブートストラップするときは、CLASSPATHに(JAXBリファレンス実装から)jaxb-xjc.jarを含める必要があります。


DynamicJAXBContextの作成に使用するAPIは次のとおりです。

例9-4 DynamicJAXBContextの作成

/**
 * Create a DynamicJAXBContext, using XML Schema as the metadata source.
 *
 * @param schemaStream
 *      java.io.InputStream from which to read the XML Schema.
 * @param resolver
 *      An org.xml.sax.EntityResolver, used to resolve schema imports.  Can be null.
 * @param classLoader
 *      The application's current class loader, which will be used to first lookup
 *      classes to see if they exist before new DynamicTypes are generated.  Can be
 *      null, in which case Thread.currentThread().getContextClassLoader() will be used.
 * @param properties
 *      Map of properties to use when creating a new DynamicJAXBContext.  Can be null.
 *
 * @return
 *      A new instance of DynamicJAXBContext.
 *
 * @throws JAXBException
 *      if an error was encountered while creating the DynamicJAXBContext.
 */
public static DynamicJAXBContext createContextFromXSD(java.io.InputStream schemaStream, EntityResolver resolver,
   ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
 
public static DynamicJAXBContext createContextFromXSD(org.w3c.dom.Node schemaDOM, EntityResolver resolver,
   ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
 
public static DynamicJAXBContext createContextFromXSD(javax.xml.transform.Source schemaSource, EntityResolver resolver,
   ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
 

注意:

classLoaderパラメータは、アプリケーションの現在のクラス・ローダーです。新しいDynamicTypesが生成される前に、クラスの存在を確認するために最初にクラスを検索する際に使用されます。ユーザーがこのパラメータにnullを渡した場合は、かわりにThread.currentThread().getContextClassLoader()が使用されます。


この例では、動的MOXyを使用して新しいオブジェクトを作成およびマーシャリングする方法を示します。

例9-5 XMLスキーマのサンプル

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="example" xmlns:myns="example" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    attributeFormDefault="qualified" elementFormDefault="qualified">
 
    <xs:element name="customer" type="myns:customer"/>
 
    <xs:complexType name="customer">
        <xs:sequence>
            <xs:element name="first-name" type="xs:string"/>
            <xs:element name="last-name" type="xs:string"/>
            <xs:element name="address" type="myns:address"/>
        </xs:sequence>
    </xs:complexType>
 
    <xs:complexType name="address">
        <xs:sequence>
            <xs:element name="street" type="xs:string"/>
            <xs:element name="city" type="xs:string"/>
            <xs:element name="province" type="xs:string"/>
            <xs:element name="postal-code" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>
 

例9-6のコード・スニペットは次のとおりです。

例9-6 アプリケーション・コードのサンプル

InputStream inputStream = myClassLoader.getSystemResourceAsStream("example/resources/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, myClassLoader, null);
 
DynamicEntity newCustomer = dContext.newDynamicEntity("example.Customer");
newCustomer.set("firstName", "George");
newCustomer.set("lastName", "Jones");
 
DynamicEntity newAddress = dContext.newDynamicEntity("example.Address");
newAddress.set("street", "227 Main St.");
newAddress.set("city", "Toronto");
newAddress.set("province", "Ontario");
newAddress.set("postalCode", "M5V1E6");
 
newCustomer.set("address", newAddress);
 
dContext.createMarshaller().marshal(newCustomer, System.out);
 

9.3.1 他のスキーマ/EntityResolverのインポート

ブートストラップに使用するXMLスキーマが別のスキーマをインポートする場合、org.xml.sax.EntityResolverを構成してインポートされたスキーマの場所を解決する必要があります。これで、EntityResolverDynamicJAXBContextFactoryに渡すことができます。

例9-7では、各タイプは各自のスキーマで定義されます。

例9-7 XMLスキーマのサンプル

<!-- customer.xsd -->
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:myns="example" xmlns:add="addressNamespace"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="example">
 
    <xs:import namespace="addressNamespace" schemaLocation="address.xsd"/>
 
    <xs:element name="customer" type="myns:customer"/>
 
    <xs:complexType name="customer">
        <xs:sequence>
            <xs:element name="first-name" type="xs:string"/>
            <xs:element name="last-name" type="xs:string"/>
            <xs:element name="address" type="add:address"/>
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>
 

EntityResolver実装を指定してインポートされたスキーマの場所を解決する必要があります。

例9-8EntityResolverを示します。

例9-8 アプリケーション・コードのサンプル

class MyEntityResolver implements EntityResolver {
 
   public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
      // Imported schemas are located in ext\appdata\xsd\
 
      // Grab only the filename part from the full path
      String filename = new File(systemId).getName();
 
      // Now prepend the correct path
      String correctedId = "ext/appdata/xsd/" + filename;
 
      InputSource is = new InputSource(ClassLoader.getSystemResourceAsStream(correctedId));
      is.setSystemId(correctedId);
 
      return is;
   }
 
}
 

次に示すように、DynamicJAXBContextの作成時に、EntityResolverを渡します。

InputStream inputStream = ClassLoader.getSystemResourceAsStream("com/foo/sales/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, new MyEntityResolver(), null, null);

別のスキーマをインポートする際に、次の例外が表示される場合があります。

Internal Exception: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document '<imported-schema-name>', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.

XJCのスキーマの正確性チェック・オプションを次のコードで無効にします。

System.setProperty("com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck", "true")

または、コマンドラインから次を入力します。

-Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrect

9.3.2 XJC外部バインディング・カスタマイズ・ファイルで生成されたマッピングのカスタマイズ

XSDからブートストラップするときに、オプションでXJCの外部バインディング・カスタマイズ・ファイル形式(.xjb)を使用して生成されるマッピングをカスタマイズできます。次の例では、動的クラスのパッケージ名がオーバーライドされ、name属性はlast-name-comma-first-nameに名前変更されます。

例9-9 custom1.xjbファイル

<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="employee.xsd" node="/xs:schema">
 
        <!-- Customize the package name that is generated for each schema -->
        <jxb:schemaBindings>
            <jxb:package name="com.acme.internal"/>
        </jxb:schemaBindings>
 
        <!-- Rename the 'name' element to 'last-name-comma-first-name' -->
        <jxb:bindings node="//xs:complexType[@name='person']">
            <jxb:bindings node=".//xs:element[@name='name']">
                <jxb:property name="last-name-comma-first-name"/>
            </jxb:bindings>
        </jxb:bindings>
 
    </jxb:bindings>
</jxb:bindings>
 

外部バインディング・カスタマイズ・ファイル形式の詳細は、http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.htmlを参照してください。


注意:

外部バインディング・カスタマイズ・ファイルを使用する場合、XMLスキーマを指すSourceオブジェクトを使用する必要があります。Sources.xjbファイルのロードにも使用され、すべて同一のシステムIDセットを持つ必要があります。


例9-10に、XSDからのブートストラップと、2つの別個の.xjbファイルを使用したマッピング生成のカスタマイズを示します。

例9-10 ブートストラップの例

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String xsd = "example/resources/xsd/employee.xsd";
String xjb1 = "example/resources/xsd/custom1.xjb";
String xjb2 = "example/resources/xsd/custom2.xjb";
 
InputStream xsdStream = classLoader.getSystemResourceAsStream(xsd);
Source xsdSource = new StreamSource(xsdStream);
// Set SYSTEM_ID to the filename part of the XSD
xsdSource.setSystemId("employee.xsd");
 
InputStream xjbStream = classLoader.getResourceAsStream(xjb1);
Source xjbSource = new StreamSource(xjbStream);
// Set SYSTEM_ID to be the same as the XSD
xjbSource.setSystemId(xsdSource.getSystemId());
 
InputStream xjbStream2 = classLoader.getResourceAsStream(xjb2);
Source xjbSource2 = new StreamSource(xjbStream2);
// Set SYSTEM_ID to be the same as the XSD
xjbSource2.setSystemId(xsdSource.getSystemId());
 
ArrayList<Source> xjbFiles = new ArrayList<Source>(2);
xjbFiles.add(xjbSource);
xjbFiles.add(xjbSource2);
 
// Put XSD and XJBs into Properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(DynamicJAXBContextFactory.XML_SCHEMA_KEY, xsdSource);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, xjbFiles);
 
// Create Context
DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("example", classLoader, properties);
 

EXTERNAL_BINDINGS_KEYの値は、単一のSourceまたはList<Source>のいずれかで、外部バインディング・カスタマイズ・ファイルを指しています。

9.4 EclipseLinkメタデータ(OXM)からのブートストラップ

XMLへのDynamicEntitiesのマッピングをさらに制御するには、かわりにEclipseLink OXMメタデータ・ファイルからブートストラップできます。このアプローチを使用すると、EclipseLinkの堅牢なマッピング・フレームワークを活用でき、XMLの複合型をそれぞれ対応するJavaにマップする方法をカスタマイズできます。次のDynamicJAXBContextFactoryのAPIを使用して、OXMファイルからブートストラップできます。

例9-11 DynamicJAXBContextの作成

/**
 * Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink OXM file as the metadata source.
 *
 * @param classLoader
 *      The application's current class loader, which will be used to first lookup classes to
 *      see if they exist before new <tt>DynamicTypes</tt> are generated. Can be <tt>null</tt>,
 *      in which case <tt>Thread.currentThread().getContextClassLoader()</tt> will be used.
 * @param properties
 *      Map of properties to use when creating a new <tt>DynamicJAXBContext</tt>.  This map must
 *      contain a key of JAXBContext.ECLIPSELINK_OXM_XML_KEY, with a value of... (see below)
 *
 * @return
 *      A new instance of <tt>DynamicJAXBContext</tt>.
 *
 * @throws JAXBException
 *      if an error was encountered while creating the <tt>DynamicJAXBContext</tt>.
 */
public static DynamicJAXBContext createContextFromOXM(ClassLoader classLoader, Map<String, ?> properties) throws JAXBException {
 

実際のOXMファイルへのリンクは、特殊キーJAXBContextProperties.OXM_METADATA_SOURCEを使用し、propertiesパラメータを介して渡されます。このキーの値は、次のいずれかの形式によるOXMメタデータ・ファイルへのハンドルとなります。

複数のOXMファイルからブートストラップするために、前述の入力のListsも受け入れられます。詳細は、DynamicJAXBContextFactoryクラスのドキュメントを参照してください。

次の例では、ClassLoaderからリソースとしてOXMファイルを取得し、結果のInputStreamを使用してDynamicJAXBContextをブートストラップします。

InputStream iStream = myClassLoader.getResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml");
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);
 
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);

9.4.1

例9-12のOXMのサンプルを使用して、動的MOXyを使用して新しいオブジェクトを作成およびマーシャリングする方法を示します。type属性をメモしておくことが大切です。基礎となるJavaクラスがないため、各プロパティのタイプを明示的に指定する必要があるからです。

例9-12 XMLスキーマのサンプル

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" package-name="example">
 
    <java-types>
        <java-type name="Customer">
            <xml-root-element name="customer"/>
            <java-attributes>
                <xml-element java-attribute="firstName" type="java.lang.String"/>
                <xml-element java-attribute="lastName" type="java.lang.String"/>
                <xml-element java-attribute="address" type="example.Address"/>
            </java-attributes>
        </java-type>
 
        <java-type name="Address">
            <java-attributes>
                <xml-element java-attribute="street" type="java.lang.String"/>
                <xml-element java-attribute="city" type="java.lang.String"/>
                <xml-element java-attribute="province" type="java.lang.String"/>
                <xml-element java-attribute="postalCode" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>
 

例9-13のコードは、次のことを示しています。

  • DynamicJAXBContextを作成するため、OXMファイルをDynamicJAXBContextFactoryに渡します。

  • 新しいDynamicEntitiesを作成してプロパティを設定します。

  • JAXBMarshallerを作成してJavaオブジェクトをXMLにマーシャリングします。

例9-13 アプリケーション・コードのサンプル

InputStream iStream = myClassLoader.getResourceAsStream("example/resources/eclipselink/eclipselink-oxm.xml");
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);
 
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);
 
DynamicEntity newCustomer = dContext.newDynamicEntity("example.Customer");
newCustomer.set("firstName", "George");
newCustomer.set("lastName", "Jones");
 
DynamicEntity newAddress = dContext.newDynamicEntity("example.Address");
newAddress.set("street", "227 Main St.");
newAddress.set("city", "Toronto");
newAddress.set("province", "Ontario");
newAddress.set("postalCode", "M5V1E6");
 
newCustomer.set("address", newAddress);
 
dContext.createMarshaller().marshal(newCustomer, System.out