7 XMLTypeデータの変換および検証

XSLTスタイルシートを使用してXMLTypeデータを変換したり、XML Schemaに対してXMLTypeインスタンスを検証したりするためのOracle SQL関数およびXMLType APIがいくつかあります。

7.1 XSL変換とOracle XML DB

組込みのOracle XML DB XSLTプロセッサを使用して、XML Schemaに基づく文書にXSL変換を適用できます。データベース内のXML固有の最適化により、必要なメモリー量が大幅に削減され、解析に関連するオーバーヘッドも削減されてネットワーク・トラフィックが緩和されます。

W3CのXSLT勧告は、XML文書の形式を別の形式に変換する方法を指定するためのXML言語を定義します。XSLT標準の詳細は、http://www.w3.org/TR/xsltを参照してください。

変換には、1つのXML Schemaから別のXML Schemaへのマッピング、またはXMLからHTMLやWMLなどの他の形式へのマッピングが含まれる場合があります。

XSL変換は、必要なメモリー量および処理量の面からコストがかかる場合があります。通常のXSLプロセッサでは、処理を開始する前に、ソース・ドキュメント全体とスタイルシートを解析してメモリーにロードする必要があります。通常、XSLプロセッサはDOMを使用して文書やスタイルシートの動的なメモリー表現を行い、任意の部分へのランダム・アクセスを可能にします。次に、XSLプロセッサは、スタイルシートをソース・ドキュメントに適用して、3番目の文書を生成します。

変換の前に文書とスタイルシートを解析してメモリーにロードするには、大量のメモリーとプロセッサ・リソースが必要です。文書のごく一部のみを変換する必要がある場合は、特に非効率です。

Oracle XML DBには、XSL変換をデータベース内で実行するXSLTプロセッサが含まれています。この方法で、XML固有の最適化を実現できます。これによって、変換の実行に必要なメモリー量が大幅に削減され、解析に関連するオーバーヘッドも削減されてネットワーク・トラフィックが緩和されます。

ただし、これらの最適化は、変換対象のソースがスキーマに基づくXML文書である場合にのみ使用できます。その場合、処理を開始する前に解析する必要はありません。Oracle XML DBで遅延ロードされた仮想DOMが、ノードへのアクセス時にオン・デマンドでコンテンツのみをロードします。処理の必要な部分の文書のみがロードされるため、必要なメモリー量が削減されます。

XMLデータは次の方法で変換できます。

  • Oracle Databaseを使用 - Oracle SQL関数のXMLtransformXMLTypeのメソッドtransform()、またはPL/SQLパッケージのDBMS_XSLPROCESSORを使用する。

  • 中間層を使用 - XSLT Processor for Javaなど、Oracle XML Developer's Kitの変換オプションを使用する。

関連項目:

これらの各XML変換方法では、ソースXML文書およびXSLスタイルシートを、XMLTypeインスタンスの形式で入力として受け取ります。SQL関数XMLtransformおよびXMLTypeメソッドtransform()では、変換の結果としてXML文書または非XML文書(HTMLなど)が発生します。しかし、PL/SQLパッケージDBMS_XSLPROCESSORでは、変換の結果は有効なXML文書であることが求められます。パッケージDBMS_XSLPROCESSORを使用した変換によって生成されるHTMLデータは、XHTMLデータ(つまり、有効なXMLデータと有効なHTMLデータ)である必要があります。

例7-1は、XSLTスタイルシートPurchaseOrder.xslの一部を示しています。完全なスタイルシートは、XSLTスタイルシートの例、PurchaseOrder.xslを参照してください。

例7-1のスタイルシートに関して、Oracle XML DB固有のものはありません。スタイルシートは、XMLType表または列に格納でき、Oracle XML DBリポジトリ内にXML Schemaに基づかないXMLデータとしても格納できます。

例7-1 XSLTスタイルシートの例: PurchaseOrder.xsl

<?xml version="1.0" encoding="WINDOWS-1252"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xdb="http://xmlns.oracle.com/xdb" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <xsl:template match="/">
    <html>
      <head/>
      <body bgcolor="#003333" text="#FFFFCC" link="#FFCC00" vlink="#66CC99" alink="#669999">
        <FONT FACE="Arial, Helvetica, sans-serif">
          <xsl:for-each select="PurchaseOrder"/>
          <xsl:for-each select="PurchaseOrder">
            <center>
              <span style="font-family:Arial; font-weight:bold">
                <FONT COLOR="#FF0000">
                  <B>PurchaseOrder </B>
                </FONT>
              </span>
            </center>
            <br/>
            <center>
              <xsl:for-each select="Reference">
                <span style="font-family:Arial; font-weight:bold">
                  <xsl:apply-templates/>
                </span>
              </xsl:for-each>
            </center>
          </xsl:for-each>
          <P>
            <xsl:for-each select="PurchaseOrder">
              <br/>
            </xsl:for-each>
            <P/>
            <P>
              <xsl:for-each select="PurchaseOrder">
                <br/>
              </xsl:for-each>
            </P>
          </P>
          <xsl:for-each select="PurchaseOrder"/>
          <xsl:for-each select="PurchaseOrder">
            <table border="0" width="100%" BGCOLOR="#000000">
              <tbody>
                <tr>
                  <td WIDTH="296">
                    <P>
                      <B>
                        <FONT SIZE="+1" COLOR="#FF0000" FACE="Arial, Helvetica, sans-serif">Internal</FONT>
                      </B>
                    </P>

                    ...

                  </td>
                  <td width="93"/>
                  <td valign="top" WIDTH="340">
                    <B>
                      <FONT COLOR="#FF0000">
                        <FONT SIZE="+1">Ship To</FONT>
                      </FONT>
                    </B>
                    <xsl:for-each select="ShippingInstructions">
                      <xsl:if test="position()=1"/>
                    </xsl:for-each>
                    <xsl:for-each select="ShippingInstructions">
                    </xsl:for-each>
 
                      ...

7.1.1 SQL関数XMLTRANSFORMおよびXMLTypeメソッドTRANSFORM()

SQL関数XMLtransformでは、XSLTスタイルシートを使用してXML文書を変換します。スタイルシートで指定したように、処理された出力をXML、HTMLなどで戻します。

図7-1に、Oracle SQL関数XMLtransformの構文を示します。この関数は引数としてXMLTypeインスタンスとXSLTスタイルシートを受け取ります。スタイルシートはXMLTypeインスタンスまたはVARCHAR2文字列リテラルのいずれかです。スタイルシートをインスタンスに適用し、XMLTypeインスタンスを戻します。

図7-1 XMLTRANSFORMの構文

図7-1の説明が続きます
「図7-1 XMLTRANSFORMの構文」の説明

Oracle SQL関数XMLtransformのかわりにXMLTypeメソッドtransform()を使用することもできます。どちらも機能は同じです。

図7-2に、XSLTスタイルシートを使用してXMLtransformがXML文書を変換する方法を示します。XSLTスタイルシートで指定したように、処理された出力をXML、HTMLなどで戻します。データベース内にXMLTypeとして格納されたXML文書の取得または生成時は、通常はXMLtransformを使用します。

図7-2 XMLTRANSFORMの使用

図7-2の説明が続きます
「図7-2 XMLTRANSFORMの使用」の説明
7.1.1.1 XMLTRANSFORMおよびXMLType.transform()の例

Oracle SQL関数XMLtransformおよびXMLTypeメソッドtransform()を使用して、XMLTypeとして格納されているXMLデータを様々な形式に変換する方法について例で説明します。

例7-2では、この章の他の例を実行するために必要なXML Schemaおよび表を設定します。ここに示したdeleteSchemaのコールは、XML Schemaの作成前に既存のスキーマがないようにするためのものです。そのようなスキーマが存在しない場合は、deleteSchemaでエラーが発生します。

例7-3ではXSLTスタイルシートを格納してからそれを取得し、Oracle SQL関数のXMLTransformでそれを使用して、例7-2で格納したXMLデータを変換します。

例7-4では、その場で作成したXSLスタイルシートでXMLTypeのメソッドtransform()を使用します。

例7-5では、XMLTransformを使用してXSLスタイルシートを適用し、HTMLコードを生成します。PL/SQLコンストラクタXDBURITypeは、Oracle XML DBリポジトリからXSLスタイルシートを読み取ります。

例7-5に、HTML結果の一部のみを示します。省略した部分は、省略記号(. . .)で示しています。図7-3に、変換された結果がWebブラウザでどのように表示されるかを示します。

例7-2 XML Schemaの登録およびXMLデータの挿入

BEGIN
  -- Delete the schema, if it already exists.
  DBMS_XMLSCHEMA.deleteSchema('http://www.example.com/schemas/ipo.xsd',4);
END;
/
BEGIN
  -- Register the schema
  DBMS_XMLSCHEMA.registerSchema(
    SCHEMAURL => 'http://www.example.com/schemas/ipo.xsd',
    SCHEMADOC => '<schema targetNamespace="http://www.example.com/IPO"
                          xmlns="http://www.w3.org/2001/XMLSchema"
                          xmlns:ipo="http://www.example.com/IPO">
                    <!-- annotation>
                      <documentation xml:lang="en">
                       International Purchase order schema for Example.com
                       Copyright 2000 Example.com. All rights reserved.
                      </documentation>
                    </annotation -->
                    <element name="purchaseOrder" type="ipo:PurchaseOrderType"/>
                    <element name="comment" type="string"/>
                    <complexType name="PurchaseOrderType">
                      <sequence>
                        <element name="shipTo"     type="ipo:Address"/>
                        <element name="billTo"     type="ipo:Address"/>
                        <element ref="ipo:comment" minOccurs="0"/>
                        <element name="items"      type="ipo:Items"/>
                      </sequence>
                      <attribute name="orderDate" type="date"/>
                    </complexType>
                    <complexType name="Items">
                      <sequence>
                        <element name="item" minOccurs="0" maxOccurs="unbounded">
                          <complexType>
                            <sequence>
                              <element name="productName" type="string"/>
                              <element name="quantity">
                                <simpleType>
                                  <restriction base="positiveInteger">
                                    <maxExclusive value="100"/>
                                  </restriction>
                                </simpleType>
                              </element>
                              <element name="USPrice"    type="decimal"/>
                              <element ref="ipo:comment" minOccurs="0"/>
                              <element name="shipDate"   type="date" minOccurs="0"/>
                            </sequence>
                            <attribute name="partNum" type="ipo:SKU" use="required"/>
                          </complexType>
                        </element>
                      </sequence>
                    </complexType>
                    <complexType name="Address">
                      <sequence>
                        <element name="name"    type="string"/>
                        <element name="street"  type="string"/>
                        <element name="city"    type="string"/>
                        <element name="state"   type="string"/>
                        <element name="country" type="string"/>
                        <element name="zip"     type="string"/>
                      </sequence>
                    </complexType>
                    <simpleType name="SKU">
                      <restriction base="string">
                        <pattern value="[0-9]{3}-[A-Z]{2}"/>
                      </restriction>
                    </simpleType>
                  </schema>',
    LOCAL     => TRUE,
    GENTYPES  => TRUE);
END;
/

-- Create table to hold XML purchase-order documents, and insert the documents
DROP TABLE po_tab;
CREATE TABLE po_tab (id NUMBER, xmlcol XMLType) 
 XMLType COLUMN xmlcol
 XMLSCHEMA "http://www.example.com/schemas/ipo.xsd"
 ELEMENT "purchaseOrder";

INSERT INTO po_tab 
  VALUES(1, XMLType(
              '<?xml version="1.0"?>
               <ipo:purchaseOrder
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns:ipo="http://www.example.com/IPO"
                 xsi:schemaLocation="http://www.example.com/IPO
                                     http://www.example.com/schemas/ipo.xsd"
                 orderDate="1999-12-01">
                 <shipTo>
                   <name>Helen Zoe</name>
                   <street>121 Broadway</street>
                   <city>Cardiff</city>
                   <state>Wales</state>
                   <country>UK</country>
                   <zip>CF2 1QJ</zip>
                 </shipTo>
                 <billTo>
                   <name>Robert Smith</name>
                   <street>8 Oak Avenue</street>
                   <city>Old Town</city>
                   <state>CA</state>
                   <country>US</country>
                   <zip>95819</zip>
                 </billTo>
                 <items>
                   <item partNum="833-AA">
                     <productName>Lapis necklace</productName>
                     <quantity>1</quantity>
                     <USPrice>99.95</USPrice>
                     <ipo:comment>Want this for the holidays!</ipo:comment>
                     <shipDate>1999-12-05</shipDate>
                   </item>
                 </items>
               </ipo:purchaseOrder>'));

例7-3 SQL関数XMLTRANSFORMを使用してXSLスタイルシートを適用

DROP TABLE stylesheet_tab;
CREATE TABLE stylesheet_tab (id NUMBER, stylesheet XMLType);
INSERT INTO stylesheet_tab 
  VALUES (1, 
          XMLType(
            '<?xml version="1.0" ?>
             <xsl:stylesheet version="1.0" 
                             xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
               <xsl:template match="*">
                 <td>
                   <xsl:choose>
                     <xsl:when test="count(child::*) > 1">
                       <xsl:call-template name="nested"/>
                     </xsl:when>
                     <xsl:otherwise>
                       <xsl:value-of select="name(.)"/>:<xsl:value-of 
                                                          select="text()"/>
                     </xsl:otherwise>
                   </xsl:choose>
                 </td>
               </xsl:template>
               <xsl:template match="*" name="nested" priority="-1" mode="nested2">
                 <b>
                   <!-- xsl:value-of select="count(child::*)"/ -->
                   <xsl:choose>
                     <xsl:when test="count(child::*) > 1">
                       <xsl:value-of select="name(.)"/>:<xsl:apply-templates 
                                                          mode="nested2"/>
                     </xsl:when>
                     <xsl:otherwise>
                       <xsl:value-of select="name(.)"/>:<xsl:value-of 
                                                          select="text()"/>
                     </xsl:otherwise>
                   </xsl:choose>
                 </b>
               </xsl:template>
             </xsl:stylesheet>'));

SELECT XMLSerialize(DOCUMENT XMLtransform(x.xmlcol, y.stylesheet)
                    AS VARCHAR2(1000))
  AS result FROM po_tab x, stylesheet_tab y WHERE y.id = 1;

これにより、次の出力が生成されます(ここでは、読みやすくするためにフォーマット出力で示しています)。

RESULT
---------------------------------------------------------
<td>
  <b>ipo:purchaseOrder:
    <b>shipTo:
      <b>name:Helen Zoe</b>
      <b>street:100 Broadway</b>
      <b>city:Cardiff</b>
      <b>state:Wales</b>
      <b>country:UK</b>
      <b>zip:CF2 1QJ</b>
    </b>
    <b>billTo:
      <b>name:Robert Smith</b>
      <b>street:8 Oak Avenue</b>
      <b>city:Old Town</b>
      <b>state:CA</b>
      <b>country:US</b>
      <b>zip:95819</b>
    </b>
    <b>items:</b>
  </b>
</td>

例7-4 一時XSLスタイルシートでのXMLTypeメソッドTRANSFORM()の使用

SELECT XMLSerialize(
         DOCUMENT
         x.xmlcol.transform(
           XMLType('<?xml version="1.0" ?>
                  <xsl:stylesheet
                      version="1.0"
                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                    <xsl:template match="*">
                      <td>
                        <xsl:choose>
                          <xsl:when test="count(child::*) > 1">
                            <xsl:call-template name="nested"/>
                          </xsl:when>
                          <xsl:otherwise>
                            <xsl:value-of
                               select =
                                 "name(.)"/>:<xsl:value-of select="text()"/>
                          </xsl:otherwise>
                        </xsl:choose>
                      </td>
                    </xsl:template>
                    <xsl:template match="*" name="nested" priority="-1"
                                  mode="nested2">
                      <b>
                        <!-- xsl:value-of select="count(child::*)"/ -->
                        <xsl:choose>
                          <xsl:when test="count(child::*) > 1">
                            <xsl:value-of select="name(.)"/>:
                            <xsl:apply-templates mode="nested2"/>
                          </xsl:when>
                          <xsl:otherwise>
                            <xsl:value-of
                               select =
                                 "name(.)"/>:<xsl:value-of select="text()"/>
                          </xsl:otherwise>
                        </xsl:choose>
                      </b>
                    </xsl:template>
                  </xsl:stylesheet>'))
         AS varchar2(1000))
  FROM po_tab x;

例7-5 XMLTRANSFORMを使用して、XDBURITypeで取得したXSLスタイルシートを適用

SELECT
  XMLTransform(
    OBJECT_VALUE, 
    XDBURIType('/source/schemas/poSource/xsl/purchaseOrder.xsl').getXML())
  FROM purchaseorder
  WHERE XMLExists('$p/PurchaseOrder[Reference="SBELL-2002100912333601PDT"]'
                  PASSING OBJECT_VALUE AS "p");
 
XMLTRANSFORM(OBJECT_VALUE, XDBURITYPE('/SOURCE/SCHEMAS/POSOURCE/XSL/PURCHASEORDER.XSL').GET
---------------------------------------------------------------------------------------------
<html xmlns:xdb="http://xmlns.oracle.com/xdb"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <head/>
  <body bgcolor="#003333" text="#FFFFCC" link="#FFCC00" vlink="#66CC99" alink="#669999">
    <FONT FACE="Arial, Helvetica, sans-serif">
      <center>
        <span style="font-family:Arial; font-weight:bold">
          <FONT COLOR="#FF0000">
            <B>PurchaseOrder </B>
          </FONT>
        </span>
      </center>
      <br/>
      <center>
        <span style="font-family:Arial; font-weight:bold">SBELL-2002100912333601PDT</span>
      </center>
      <P>
        <br/>
        <P/>
        <P>
          <br/>
        </P>
      </P>
      <table border="0" width="100%" BGCOLOR="#000000">
        <tbody>
          <tr>
            <td WIDTH="296">
              <P>
                <B>
                  <FONT SIZE="+1" COLOR="#FF0000" FACE="Arial, Helvetica,
                        sans-serif">Internal</FONT>
                </B>
              </P>
              <table border="0" width="98%" BGCOLOR="#000099">
                                                     . . .
              </table>
            </td>
            <td width="93">
            </td>
            <td valign="top" WIDTH="340">
              <B>
                <FONT COLOR="#FF0000">
                  <FONT SIZE="+1">Ship To</FONT>
                </FONT>
              </B>
              <table border="0" BGCOLOR="#999900">
                . . .
              </table>
            </td>
          </tr>
        </tbody>
      </table>
      <br/>
      <B>
        <FONT COLOR="#FF0000" SIZE="+1">Items:</FONT>
      </B>
      <br/>
      <br/>
      <table border="0">
        . . .
      </table>
    </FONT>
  </body>
</html>
 
1 row selected.

7.1.2 DBUriサーブレットを使用したXSL変換

DBUriサーブレットで生成されたXMLコンテンツに、XSL変換を適用できます。

図7-3に、このような変換の結果を示します。URLは次のとおりです(ここでは、分割して切り捨てられています)。

http://localhost:8080/oradb/SCOTT/PURCHASEORDER/ROW/PurchaseOrder[Reference="SBELL-2003030912333601PDT"]
contenttype=text/html&transform=/home/SCOTT/xsl/purchaseOrder.xsl...

パラメータtransformが存在するため、DBUriサーブレットはSQL関数XMLTransformを使用して、/home/SCOTT/xsl/purchaseOrder.xslにあるXSLスタイルシートを、メインのURLで識別されるPurchaseOrder文書に適用します。変換の結果、HTMLコードがブラウザに返されて表示されます。また、URLでは、パラメータcontentTypeを使用して、最後の文書のMIMEタイプがtext/htmlになることを指定します。

図7-3 DBUriサーブレットを使用したPurchaseOrderのデータベースXSL変換

図7-3の説明が続きます
「図7-3 DBUriサーブレットを使用したPurchaseOrderのデータベースXSL変換」の説明

図7-4に、HTML文書として表示されたdepartments表を示します。これを実行するためにコードは必要ありません。必要なのは、SQL/XML関数に基づくXMLTypeビュー、業界標準のXSLスタイルシート、およびDBUriサーブレットのみです。

図7-4 DBUriサーブレットを使用したDepartments表のデータベースXSL変換

図7-4の説明が続きます
「図7-4 DBUriサーブレットを使用したDepartments表のデータベースXSL変換」の説明

7.2 XMLTypeインスタンスの検証

多くの場合、特定のXML文書が整形式であることに加えて、その文書が特定のXML Schemaに準拠するかどうか(特定のXML Schemaに対して妥当であるかどうか)を認識する必要があります。

バイナリXMLとして格納されたXML Schemaに基づくデータは、挿入または更新時に必ず自動的に完全に検証されます。この検証にはDOMの構築は必要ありません。これはストリーミングを使用して実行され、メモリーの使用を効率的にかつ最小限にします。

オブジェクト・リレーショナル形式で格納されるXMLTypeデータの場合は、完全な検証にDOMの構築が必要なため、メモリー管理の面でコストがかかります。このため、Oracle XML DBは、オブジェクト・リレーショナル形式で格納されるデータが挿入または更新されるときに、完全な検証を自動的には実行しません。

ただし、オブジェクト・リレーショナル形式で格納するためにXMLデータを分解する過程で、Oracle XML DBはXML文書の構造がXML Schemaから導出されたSQLデータ型の定義に準拠しているか確認するため、部分的な検証を実行します。

オブジェクト・リレーショナル形式で格納されたXMLTypeデータに完全な検証が必要であれば、クライアントで検証してからデータベースに挿入したり、更新することを考慮してください。

XML文書で記録された有効性状態を完全に検証または操作するために、次のものを使用できます。

  • Oracle SQL関数XMLIsValidおよびXMLTypeメソッドIsSchemaValid() - 検証プロセスを無条件に実行します。検証状態は記録されません。次の値を戻します。

    • 文書が有効と判断される場合は1

    • 文書が無効と判断される場合または文書の妥当性が判断できない場合は、0

  • XMLTypeメソッドSchemaValidate() - 検証のステータスが0 (デフォルト)の場合、検証プロセスを実行します。文書が有効だと判断されると、検証ステータスは1に設定されます(それ以外の場合、ステータスは0のままです)。

  • XMLTypeメソッドisSchemaValidated(): XMLTypeインスタンスの記録済の検証状態を戻します。

  • XMLTypeメソッドsetSchemaValidated(): XMLTypeインスタンスの検証状態を設定(記録)します。

検証状態では、次のように妥当性がわかります。

  • 1は、文書が有効と判断されることを示します。

  • 0は、文書の妥当性が不明であることを示します。検証チェックで文書が無効であるように判断される可能性がありますが、文書が無効であると記録されるわけではありません。検証状態が0と記録された場合にわかるのは、文書の妥当性が不明であることのみです。

関連項目:

7.2.1 XML Schemaの部分検証と全体検証

XML Schemaに基づく文書をデータベースに挿入するとき、これらを部分的または完全に検証できます。

7.2.1.1 部分検証

バイナリXML記憶域の場合、XML Schemaに基づく文書がXMLType表または列に挿入されるたびに、Oracle XML DBによりこれらの文書の全体検証が実行されます。オブジェクト・リレーショナルXML記憶域の場合は、部分検証のみが実行されます。これは、オブジェクト・リレーショナル記憶域の場合、XML Schemaの完全な検証はパフォーマンスの面で非常にコストが高くなるためです。

部分検証では、文書内に必須の要素および属性がすべて存在すること、および予期しない要素または属性のないことが確認されるのみです。つまり、XML文書の構造がXML Schemaから導出されたSQLデータ型定義に準拠していることのみが確認されます。部分検証では、インスタンス・ドキュメントがXML Schemaに完全に準拠しているかどうかは確認されません。

例7-6に、オブジェクト・リレーショナル形式で格納されている表PurchaseOrderにXML文書を挿入する間に部分検証が失敗した例を示します。

例7-6 不適切なXML文書の挿入時に発生するエラー(部分検証)

INSERT INTO purchaseorder
  VALUES(XMLType(bfilename('XMLDIR', 'InvalidElement.xml'),
                 nls_charset_id('AL32UTF8')));
  VALUES(XMLType(bfilename('XMLDIR', 'InvalidElement.xml'),
         *
ERROR at line 2:
ORA-30937: No schema definition for 'UserName' (namespace '##local') in parent
'/PurchaseOrder'
7.2.1.2 全体検証

XML Schemaに基づくデータをバイナリXML記憶域にロードすると、ターゲットXML Schemaに対してデータの全体検証が行われます。オブジェクト・リレーショナルXMLType記憶域の場合、いつでも全体検証を強制できます。

全体検証を強制するには、次のいずれかを使用します。

  • 表レベルのCHECK制約

  • PL/SQLのBEFORE INSERTトリガー

どちらの方法でも、妥当なXML文書のみがXMLType表に格納されることが保証されます。

TABLE CHECK制約を使用する方法のメリットは、コーディングが簡単なことです。この方法のデメリットは、この方法がOracle SQL関数XMLisValidに基づいているため、XML文書が妥当であるかどうかしか示されないことです。XML文書が妥当でない場合、TABLE CHECK制約では、その理由についての情報を提供できません。

BEFORE INSERTトリガーの場合は、必要なコーディングが表レベルの制約よりわずかに多くなります。このトリガーでは、XMLTypeschemaValidate()メソッドを起動してXML文書を検証します。schemaValidate()を使用するメリットは、例外が発生した場合にそのインスタンス・ドキュメントの問題点に関する追加情報が提供されることです。また、BEFORE INSERTトリガーを使用すると、無効な文書が存在する場合に適切な処置を行うことができます。

7.2.1.2.1 XML Schemaの全体検証による処理時間とメモリーの消費

バイナリXML記憶域を使用していない場合は、XML Schemaの全体検証を実行すると、多くの処理時間とメモリーが消費されます。このため、XML Schemaの全体検証は、必要なときにのみ実行してください。

XML文書を検証するアプリケーションを使用する場合は、全体検証に関連するオーバーヘッドを回避することによって、バイナリ以外のXML記憶域での全体のスループットを向上させることができます。受信するXML文書の妥当性について確認できない場合は、スキーマに対して妥当なXML文書のみがXMLType表または列に含まれていることをデータベースで確認できます。

例7-7に、CHECK制約をXMLType表に追加してXML Schemaの全体検証を実行する方法を示します。例7-7のXML文書InvalidReferenceは、XML Schemaに従った妥当なXML文書ではありません。XML Schemaでは、Reference要素に関連付けられたテキスト・ノードの最小長を18文字とすることが定義されています。この例に示す文書では、ノードの値はSBELL-20021009で、14文字の長さしかありません。部分検証ではこのエラーは捕捉されません。制約またはトリガーがないかぎり、この文書をデータベースに挿入することが可能です。

例7-7 CHECK制約を使用したXML Schemaの全体検証の実施

ALTER TABLE purchaseorder
  ADD CONSTRAINT validate_purchaseorder
  CHECK (XMLIsValid(OBJECT_VALUE) = 1);
 
Table altered.
 
INSERT INTO purchaseorder
  VALUES (XMLType(bfilename('XMLDIR', 'InvalidReference.xml'),
                  nls_charset_id('AL32UTF8')));

INSERT INTO purchaseorder
*
 
ERROR at line 1:
ORA-02290: check constraint (QUINE.VALIDATE_PURCHASEORDER) violated

疑似列OBJECT_VALUEを使用すると、トリガー内からXMLType表のコンテンツにアクセスできます。例7-8は、BEFORE INSERTトリガーを使用して、XMLType表に挿入されるデータが、指定されたXML Schemaに準拠していることを検証する方法を示しています。

例7-8 BEFORE INSERTトリガーを使用したXML Schemaの全体検証の実施

CREATE OR REPLACE TRIGGER validate_purchaseorder
   BEFORE INSERT ON purchaseorder
   FOR EACH ROW
BEGIN
  IF (:new.OBJECT_VALUE IS NOT NULL) THEN :new.OBJECT_VALUE.schemavalidate();
  END IF;
END;
/

INSERT INTO purchaseorder  VALUES (XMLType(bfilename('XMLDIR', 'InvalidReference.xml'),
                  nls_charset_id('AL32UTF8')));
  VALUES (XMLType( bfilename('XMLDIR', 'InvalidReference.xml'),
          *
ERROR at line 2:
ORA-31154: invalid XML document
ORA-19202: Error occurred in XML processing
LSX-00221: "SBELL-20021009" is too short (minimum length is 18)
LSX-00213: only 0 occurrences of particle "sequence", minimum is 1
ORA-06512: at "SYS.XMLTYPE", line 354
ORA-06512: at "QUINE.VALIDATE_PURCHASEORDER", line 3
ORA-04088: error during execution of trigger 'QUINE.VALIDATE_PURCHASEORDER'

7.2.2 XMLTypeとして格納されたXMLデータの検証例

ここでの例は、Oracle SQL関数XMLIsValidと、XMLTypeメソッドisSchemaValid()およびschemaValidate()を使用して、Oracle XML DBにXMLTypeとして格納されているXMLデータを検証する方法を示しています。

例7-9および例7-10に、PL/SQLメソッドisSchemaValid()を使用して、XML Schemaに対してXMLインスタンスを検証する方法を示します。

XMLTypeメソッドschemaValidate()INSERTおよびUPDATEトリガー内で使用すると、表に格納されているすべてのインスタンスがXML Schemaに対して検証されたことを確認できます。例7-11に、これを示します。

例7-12では、Oracle SQL関数XMLIsValidを使用して、次の操作を実行します。

  • XMLTypeインスタンスが指定されたXML Schemaに準拠していることの確認

  • CHECK制約を使用した、受信したXML文書が妥当であることの確認

注意:

XMLTypeインスタンスの検証で示した検証の関数およびプロシージャを使用すると、簡単に検証チェックを実行できます。これらのうち、schemaValidateのみが、エラーの発生時に検証が失敗した理由を示します。

例7-9 SQLでメソッドISSCHEMAVALID()を使用したXMLの検証

SELECT x.xmlcol.isSchemaValid('http://www.example.com/schemas/ipo.xsd',
                              'purchaseOrder')
    FROM po_tab x;

例7-10 PL/SQLでメソッドISSCHEMAVALID()を使用したXMLの検証

DECLARE
  xml_instance XMLType;
BEGIN
  SELECT x.xmlcol INTO xml_instance FROM po_tab x WHERE id = 1;
  IF xml_instance.isSchemaValid('http://www.example.com/schemas/ipo.xsd') = 0
    THEN raise_application_error(-20500, 'Invalid Instance');
    ELSE DBMS_OUTPUT.put_line('Instance is valid');
  END IF;
END;
/
Instance is valid
 
PL/SQL procedure successfully completed.

例7-11 トリガー内でメソッドSCHEMAVALIDATE()を使用したXMLの検証

DROP TABLE po_tab;
CREATE TABLE po_tab OF XMLType 
  XMLSCHEMA "http://www.example.com/schemas/ipo.xsd" ELEMENT "purchaseOrder";

CREATE TRIGGER emp_trig BEFORE INSERT OR UPDATE ON po_tab FOR EACH ROW

DECLARE 
  newxml XMLType;
BEGIn
  newxml := :new.OBJECT_VALUE;
  XMLTYPE.schemavalidate(newxml);
END;
/

例7-12 CHECK制約内でXMLISVALIDを使用したXMLの妥当性のチェック

DROP TABLE po_tab;
CREATE TABLE po_tab OF XMLType 
   (CHECK(XMLIsValid(OBJECT_VALUE) = 1))
   XMLSCHEMA "http://www.example.com/schemas/ipo.xsd" ELEMENT "purchaseOrder";