ヘッダーをスキップ
Oracle XML Developer's Kitプログラマーズ・ガイド
11gリリース1(11.1)
E05676-02
  目次
目次
索引
索引

戻る
戻る
 
次へ
次へ
 

18 XML Parser for Cの使用

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

XML Parser for Cの概要

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


関連項目:

DOMとSAXを使用したXML解析は、「XML Parsing for Javaの概要」を参照してください。概要の大部分の情報は特定の言語に依存しておらず、Cにも適用されます。

前提条件

Oracle XML Parser for Cは、XML文書を読み取り、DOMまたはSAX APIを使用して、そのコンテンツと構造へのプログラム・アクセスを可能にします。このパーサーは検証モードまたは非検証モードで使用できます。またPull Parserも使用可能です。

この章では、次のテクノロジを十分に理解していると想定します。

前述のテクノロジの概要説明が必要な場合は、「はじめに」の「関連ドキュメント」にあるXMLの資料を参照してください。

標準と仕様

XML 1.0はW3C勧告です。C XDK APIはXML 1.0(Second Edition)をフル・サポートしています。Second Editionの仕様は次のURLにあります。

http://www.w3.org/TR/2000/REC-xml-20001006

DOMレベル1、レベル2およびレベル3の仕様はW3C勧告です。C XDK APIはDOMレベル1および2をフル・サポートしていますが、レベル3はサポートしていません。次のURLに、3つのレベルすべての仕様へのリンクがあります。

http://www.w3.org/DOM/DOMTR

SAXにはバージョン1.0と2.0があり、バージョン1.0は非推奨です。SAXはW3C仕様ではありません。C XDK APIはSAX 1.0および2.0の両方をフル・サポートしています。次のURLに、SAXのドキュメントがあります。

http://www.saxproject.org

XML名前空間はW3C勧告です。仕様は次のURLにあります。

http://www.w3.org/TR/REC-xml-names

関連項目:

XDKがサポートする標準の概要は、第31章「XDK標準」を参照してください。

XML Parser for Cの使用

Oracle XML Parser for Cは、XML文書が整形式であるかどうか、およびDTDに対して妥当であるかどうか(オプション)を確認します。アプリケーションでは、DOMまたはSAX APIを使用して、解析されたデータにアクセスできます。

Parser API for Cの概要

XML解析APIの中核は、XML、DOM、SAXの各APIです。表18-1に、これらのAPIのインタフェースを示します。APIに関するドキュメントの詳細は、『Oracle XML APIリファレンス』を参照してください。

表18-1 XML、DOM、SAXの各APIのインタフェース

パッケージ インタフェース 関数名の表記規則

XML

このパッケージは、1つのXMLインタフェースを実装します。インタフェースは次のタスクの関数を定義します。

  • コンテキストの作成と破棄。最上位のXMLコンテキスト(xmlctx)は、連動するXMLコンポーネント間で共通の情報を共有します。

  • XML文書とDTDの作成と解析。

関数名は文字列Xmlで始まります。

APIのドキュメントは、『Oracle Database XML C APIリファレンス』を参照してください。

DOM

このパッケージは、解析済XMLへのプログラム・アクセスを提供します。このパッケージは次のインタフェースを実装します。

  • Attrは、XML属性用のget関数およびset関数を定義します。

  • CharacterDataは、文字データを操作する関数を定義します。

  • Documentは、XMLノードを作成する関数、XML文書に関する情報を取得する関数、文書のDTDを設定する関数を定義します。

  • DocumentTypeは、DTD用のget関数を定義します。

  • Elementは、XML要素用のget関数およびset関数を定義します。

  • Entityは、XMLエンティティ用のget関数を定義します。

  • NameNodeMapは、名前付きノード用のget関数を定義します。

  • Nodeは、XMLノード用のget関数およびset関数を定義します。

  • NodeListは、ノード・リストを解放し、リストからノードを取得する関数を定義します。

  • Notationは、ノードからシステムおよびパブリックIDを取得する関数を定義します。

  • ProcessingInstructionは、命令を処理するget関数およびset関数を定義します。

  • Textは、テキスト・ノードを2つに分割する関数を定義します。

関数名は文字列XmlDomで始まります。

APIのドキュメントは、『Oracle Database XML C APIリファレンス』を参照してください。

SAX

このパッケージは、解析済XMLへのプログラム・アクセスを提供します。このパッケージはSAXインタフェースを実装します。SAXインタフェースは、SAXイベントの通知を受け取る関数を定義します。

関数名は文字列XmlSaxで始まります。

APIのドキュメントは、『Oracle Database XML C APIリファレンス』を参照してください。

XML Pull Parser

XMLイベントとはXML文書の表現で、開始タグ、終了タグ、コメントなどのような一連のイベントで文書が表現されるSAXイベントに類似しています。SAXイベントはパーサー(生産者)によって駆動され、XMLイベントはアプリケーション(消費者)によって駆動されるという点で異なります。

関数名は文字列XmlEvで始まります。

APIのドキュメントは、『Oracle Database XML C APIリファレンス』を参照してください。


XML Parser for Cのデータ型

C XDKのデータ型の完全なリストは、『Oracle XML APIリファレンス』を参照してください。表18-2に、XML Parser for Cで使用するデータ型を示します。

表18-2 XML Parser for Cで使用するデータ型

データ型 説明

oratext

文字列ポインタ

xmlctx

マスターXMLのコンテキスト

xmlsaxcb

SAXコールバック構造(SAXのみ)

ub4

32ビット以上の符号なし整数

uword

ネイティブな符号なし整数


XML Parser for Cのデフォルト

XML Parser for Cのデフォルトは、次のとおりです。

  • キャラクタ・セットのエンコーディングはUTF-8です。ドキュメントがすべてASCII形式である場合は、パフォーマンス向上のために、エンコーディングをUS-ASCIIに設定することをお薦めします。

  • パーサーのメッセージは、エラー・ハンドラが提供されていないかぎり、stderrに出力されます。

  • パーサーは入力文書が整形式であるかどうかを確認しますが、妥当であるかどうかは確認しません。プロパティ"validate"を設定することで、入力の妥当性を検証できます。


    注意:

    シングルバイト・キャラクタ・セット(US-ASCII、ISO 8859キャラクタ・セットのいずれか)のみを使用している場合、明示的にデフォルトのエンコーディングを設定することをお薦めします。UTF-8などのマルチバイト・キャラクタ・セットを使用した場合よりパフォーマンスが向上します。

  • パーサーは、空白を処理するときにXML 1.0に準拠します。つまり、パーサーはアプリケーションにすべての空白を報告し、どの空白が無視できる空白であるかを示します。ただし、アプリケーションによっては、プロパティ"discard-whitespace"を設定し、要素の終了タグと次の要素の開始タグの間のすべての空白を削除する方が適切な場合もあります。


関連項目:


XML Parser for Cのコール順序

図18-1に、XML Parser for Cのコール順序を示します。

図18-1 XML Parser for Cのコール順序

図18-1の説明が続きます
「図18-1 XML Parser for Cのコール順序」の説明

XML Parser for Cの使用: 基本プロセス

アプリケーションで次の手順を実行します。

  1. XmlCreate()関数を使用して解析プロセスを初期化します。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

    xmlctx     *xctx;
    ...
    xctx = XmlCreate(&ecode, (oratext *) "namespace_xctx", NULL);
    
  2. 入力項目を解析します。入力項目はXML文書または文字列バッファのどちらかです。

    DOMを使用して解析する場合、XmlLoadDom()関数をコールします。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

    xmldocnode *doc;
    ...
    doc = XmlLoadDom(xctx, &ecode, "file", DOCUMENT,
                     "validate", TRUE, "discard_whitespace", TRUE, NULL);
    

    SAXを使用して解析する場合、XmlLoadSax()関数をコールします。次のサンプル・コード部分は、SAXNamespace.cからの抜粋です。

    xmlerr      ecode;
    ...
    ecode = XmlLoadSax(xctx, &sax_callback, &sc, "file", DOCUMENT,
                       "validate", TRUE, "discard_whitespace", TRUE, NULL);
    

    Pull Parserを使用する場合は、イベント・コンテキストを作成して解析する文書ロードするために次の手順が含まれます。

    evctx = XmlEvCreatePPCtx(xctx, &xerr, NULL);
    XmlEvLoadPPDoc(xctx, evctx, "File", input_filenames[i], 0, NULL);
    
  3. DOMインタフェースを使用する場合は、次の手順が含まれます。

    • XmlLoadDom()関数を使用してXmlDomGetDocElem()をコールします。この手順により他のDOM関数がコールされます。通常、これらの他の関数は、必要に応じてDOM文書を出力するノード関数または出力関数です。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

      printElements(xctx, XmlDomGetDocElem(xctx, doc));
      
    • XmlFreeDocument()関数をコールし、解析プロセス中に作成されたすべてのデータ構造を削除します。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

      XmlFreeDocument(xctx, doc);
      

    SAXインタフェースを使用する場合は、次の手順が含まれます。

    • 次のようなコールバック関数でXmlLoadSax()のコール結果を処理します。

      xmlsaxcb saxcb = {
       UserStartDocument,  /* user's own callback functions */
       UserEndDocument,
       /* ... */
      };
      
      if (XmlLoadSax(xctx, &saxcb, NULL, "file", "some_file.xml", NULL) != 0)
        /* an error occured */
      
    • コールバック関数を登録します。不要なSAXコールバック関数は、すべてNULLに設定できることに注意してください。

    Pull Parserを使用する場合は、次を使用してイベントを繰り返します。

    cur_event = XmlEvNext(evctx);
    

    Get APIを使用してイベントに関する情報を取得します。

  4. XmlFreeDocument()を使用して、解析中に使用されたメモリーと構造を削除します。XMLFreeDocument()またはXMLDestroy()をコールするまで、SAXコールバック関数に渡されたパラメータに割り当てられたメモリー、またはDOM解析ツリーとともに格納されたノードおよびデータに割り当てられたメモリーは解放されません。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

    XmlFreeDocument(xctx, doc);
    

    手順2に戻るか、次の手順に進みます。

    Pull ParserはXmlEvCleanPPCtx()をコールして解析中に使用されたメモリーと構造を公開します。アプリケーションは、別の文書を解析するためにXmlEvLoadPPDoc()を再度コールできます。または、Pull Parserコンテキストを再度使用できなくなった後は、XMLEvDestroyPPCtx()をコールできます。

    XmlEvCleanPPCtx(xctx, evctx);
    ...
    XmlEvDestroyPPCtx(xctx, evctx);
    
  5. XmlDestroy()を使用して解析プロセスを終了します。次のサンプル・コード部分は、DOMNamespace.cからの抜粋です。

    (void) XmlDestroy(xctx);
    

    初期化から終了までのコール順序の途中でスレッドが分岐している場合、アプリケーションで不適切な動作および結果が発生する場合があります。

独自のメモリー割当てにはメモリー・コールバック関数XML_ALLOC_FおよびXML_FREE_Fを使用できます。その場合、両方の関数を指定します。

XML Parser for Cのデモ・プログラムの実行

$ORACLE_HOME/xdk/demo/c/(UNIXの場合)および%ORACLE_HOME%\xdk\demo\c(Windowsの場合)ディレクトリには、DOMインタフェースおよびSAXインタフェースによるXML Parser for Cの使用方法を示すXMLアプリケーションがあります。表18-3に、デモを示します。

makeユーティリティでは、ソース・ファイルfileName.cをコンパイルし、デモ・プログラムfileNameおよび出力ファイルfileName.outを作成できます。予期される出力は、fileName.stdです。

表18-3 Cパーサーのデモ

ディレクトリ 内容 デモ

dom

DOMNamespace.c
DOMSample.c
FullDom.c
FullDom.xml
NSExample.xml
Traverse.c
XPointer.c
class.xml
cleo.xml
pantry.xml

次のデモ・プログラムではDOM APIを使用します。

  • DOMNamespaceプログラムでは、DOM APIに名前空間による拡張を使用します。NSExample.xmlのすべての要素と属性、および名前空間に関するすべての情報が出力されます。

  • DOMSampleプログラムでは、DOM APIを使用して、戯曲(cleo.xml)の概要(XML要素のACTおよびSCENE)を表示します。cleo.xml文書には、『The Tragedy of Antony and Cleopatra』(シェイクスピア戯曲のXMLバージョン)が入っています。

  • FullDomプログラムでは、DOMインタフェース全体の使用例を示します。すべてのコールを実行します。プログラムでは、エンティティの使用方法を示すFullDom.xmlを入力として受け入れます。

  • Traverseプログラムでは、DOMイテレータ、ツリー・ウォーカーおよび範囲の使用方法を示します。プログラムでは、大学の計算法科目を示すclass.xml文書を入力として受け入れます。

  • XPointerプログラムでは、pantry.xml<pantry>要素の子を検索して、XMLポインタ言語の使用方法を示します。

sax

NSExample.xml
SAXNamespace.c
SAXSample.c
cleo.xml

次のデモ・プログラムではSAX APIを使用します。

  • SAXNamespaceプログラムでは、SAX APIに名前空間による拡張を使用します。NSExample.xmlのすべての要素と属性、および名前空間に関するすべての情報が出力されます。

  • SAXSampleプログラムでは、SAX APIを使用して、戯曲(cleo.xml)内の、指定されたワードを含むすべてのせりふを表示します。ワードを指定しない場合は、ワード"death"が使用されます。cleo.xml文書には、『The Tragedy of Antony and Cleopatra』(シェイクスピア戯曲のXMLバージョン)が入っています。


サンプル・プログラムのコンパイルおよび実行方法を説明するドキュメントは、同じディレクトリ内のREADMEにあります。基本手順は次のとおりです。

  1. ディレクトリを$ORACLE_HOME/xdk/demo/c(UNIXの場合)または%ORACLE_HOME%\xdk\demo\c(Windowsの場合)に変更します。

  2. 「UNIXでのC XDK環境変数の設定」および「WindowsでのC XDK環境変数の設定」の説明に従って、環境変数が設定されていることを確認します。

  3. システム・プロンプトでmake(UNIXの場合)またはMake.bat(Windowsの場合)を実行します。makeユーティリティでは、ディレクトリをデモ・サブディレクトリに変更し、makeを実行して次の処理を行います。

    1. ccユーティリティでCソース・ファイルをコンパイルします。たとえば、$ORACLE_HOME/xdk/demo/c/domディレクトリのMakefileには、次の行があります。

      $(CC) -o DOMSample $(INCLUDE) $@.c $(LIB)
      
    2. 各デモ・プログラムを実行し、出力をファイルにリダイレクトします。たとえば、$ORACLE_HOME/xdk/demo/c/domディレクトリのMakefileには、次の行があります。

      ./DOMSample > DOMSample.out
      
  4. プログラムごとに*.stdファイルと*.outファイルを比較します。*.stdファイルには、各プログラムの予期される出力が入っています。たとえば、DOMSample.stdには、DOMSampleの実行で予期される出力が入っています。

XML Parser for Cコマンドライン・ユーティリティの使用

$ORACLE_HOME/bin(UNIXの場合)または%ORACLE_HOME%\bin(Windowsの場合)にあるxmlユーティリティは、XML文書を解析するコマンドライン・インタフェースです。XML文書が整形式であるか、および妥当であるかを確認します。

xmlを使用するには、「UNIXでのC XDK環境変数の設定」および「WindowsでのC XDK環境変数の設定」の説明に従って、環境が設定されていることを確認します。

コマンドラインで次の構文を使用して、xmlを起動します。Windowsではxml.exeを使用します。

xml [options] [document URI]
xml -f [options] [document filespec]

表18-4に、コマンドライン・オプションを示します。

表18-4 C XML Parserのコマンドライン・オプション

オプション 説明

-B BaseURI

XSLTプロセッサのベースURIを設定します。http://pqr/xsl.txtのベースURIでは、pqr.txthttp://pqr/pqr.txtに解決します。

-c

入力文書が整形式であるかどうかを確認しますが、妥当であるかどうかは確認しません。

-e encoding

デフォルトの入力ファイルのエンコーディング(インコーディング)を指定します。

-E encoding

DOM/SAXのエンコーディング(アウトコーディング)を指定します。

-f file

ファイルをURIではなくfilespecとして解析します。

-G xptr_exprs

ファイルに指定されたXPointerスキームの例を評価します。

-h

使用方法のヘルプおよびコマンドライン・オプションの基本リストを表示します。

-hh

コマンドライン・オプションの詳細なリストを表示します。

-i n

XSLTプロセスを繰り返す回数を指定します。

-l language

エラー報告用の言語を指定します。

-n

DOMを検索し、要素の数を報告します。次に、出力例を示します。

ELEMENT       1
 PCDATA       1
    DOC       1
  TOTAL       3 * 60 = 180

-o XSLoutfile

XSLTプロセッサの出力ファイルを指定します。

-p

解析後の文書とDTDの構造を出力します。たとえば、ルート要素<greeting>hello</greeting>は次のように出力されます。

+---ELEMENT greeting     +---PCDATA "hello"

-P

ルート要素から文書を出力します。たとえば、ルート要素<greeting>hello</greeting>は次のように出力されます。

<greeting>hello</greeting>

-PP

ルート・ノード(DOC)からXML宣言を含めて出力します。

-PE encoding

-Pまたは-PPの出力のエンコーディングを指定します。

-PX

出力にXML宣言を含めます。

-s stylesheet

XSLTスタイルシートを指定します。

-v

XDKパーサーのバージョンを表示した後、終了します。

-V var value

CXSLTの最上位の変数をテストします。

-w

すべての空白を保持します。

-W

警告の後で解析を停止します。

-x

SAXインタフェースを実行し、文書を出力します。次に出力例を示します。

StartDocumentXMLDECL version='1.0' encoding=FALSE<greeting>    "hello"</greeting>EndDocument

XML Parser for Cコマンドライン・ユーティリティの使用: 例

$ORACLE_HOME/xdk/demo/cにある様々なXMLファイルに対してxmlをテストできます。例18-1に、NSExample.xmlの内容を示します。

例18-1 NSExample.xml

<!DOCTYPE doc [
<!ELEMENT doc (child*)>
<!ATTLIST doc xmlns:nsprefix CDATA #IMPLIED>
<!ATTLIST doc xmlns CDATA #IMPLIED>
<!ATTLIST doc nsprefix:a1 CDATA #IMPLIED>
<!ELEMENT child (#PCDATA)>
]>
<doc nsprefix:a1 = "v1" xmlns="http://www.w3c.org"
     xmlns:nsprefix="http://www.oracle.com">
<child>
This element inherits the default Namespace of doc.
</child>
</doc>

次の例に示すように、このファイルを解析し、要素数を計算し、DOMツリーを表示できます。

xml -np NSEample.xml > xml.out

次の例に、出力を示します。

例18-2 xml.out

   ELEMENT       2
    PCDATA       1
       DOC       1
       DTD       1
  ELEMDECL       2
  ATTRDECL       3
     TOTAL      10 * 112 = 1120
+---ELEMENT doc [nsprefix:a1='v1'*, xmlns='http://www.w3c.org'*, xmlns:nsprefix=
'http://www.oracle.com'*]
    +---ELEMENT child
        +---PCDATA "
This element inherits the default Namespace of doc.
"

DOM API for Cの使用

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

C APIに対応するXML文書のデータ・エンコーディングの制御

XMLデータは多くのエンコーディングに存在します。XMLエンコーディングは次のように制御できます。

  • 自己記述的でないファイルに対して前提とするデフォルト・エンコーディングの指定

  • DOMまたはSAXの表示用エンコーディングの指定

  • DOMがシリアライズされている場合の再エンコーディング

入力XMLデータは常にエンコードされています。UTF-16など、エンコーディングによっては完全に自己記述的なものがあり、この場合は実際のデータの開始前に特定のBOMが必要です。文書のXMLDeclまたはMIMEヘッダーでもエンコーディングを指定できます。アプリケーションで特定のエンコーディングを決定できない場合、デフォルトの入力エンコーディングが適用されます。デフォルトを指定していない場合、ASCIIプラットフォームではUTF-8、EBCDICプラットフォームではUTF-Eが想定されます。

APIでは、入力文書のエンコーディング・データが不正である場合に備えて、対策が取られています。たとえば、encoding=asciiと記述されているXMLDeclが含まれるASCII文書がEBCDICに変換されたとします。新しいEBCDIC文書には、(EBCDIC内に)文書がASCIIであると誤って主張するXMLDeclが含まれます。XMLデータを再エンコードするプログラムの正しい動作は、XMLDeclの変換ではなく、これを再生成することです。XMLDeclは、データそのものではなく、メタデータです。しかし、このルールは無視されることが多く、そのため不正な文書が生成されます。この問題を回避するため、APIには入力エンコーディングの強制的な設定を可能にする追加フラグが提供されており、不正なXMLDeclの問題が解決されています。

入力エンコーディングを決定するための優先順位ルールは、次のとおりです。

  1. ユーザーが指定した強制エンコーディング


    注意:

    競合が存在する場合、強制エンコーディングによって致命的エラーになる可能性があります。たとえば、入力文書がUTF-16であり、UTF-16 BOMで始まっているにもかかわらず、ユーザーが強制UTF-8エンコーディングを指定したとします。この場合、パーサーによって競合に関するエラーが発行されます。

  2. プロトコル仕様(HTTPヘッダーなど)

  3. XMLDecl仕様

  4. ユーザーのデフォルト入力エンコーディング

  5. デフォルト(ASCIIプラットフォームではUTF-8、EBCDICプラットフォームではUTF-E)

アプリケーションでは、入力エンコーディングが決定された後、文書の解析およびデータの表示が可能になります。表示用エンコーディングを選択できます。元の入力エンコーディングにかかわらず、データにはこの表示用エンコーディングが実行されます。

アプリケーションでシリアライズ形式でDOMを書き戻す時点で、表示用データの再エンコードを選択できます。したがって、シリアライズされた文書を任意のエンコーディングにすることができます。

ヌル文字終了および長さエンコードのC API関数の使用

Cにおけるネイティブな文字列表現はヌル文字で終了します。そのため、プライマリDOMインタフェースではヌル文字で終了する文字列が使用され、戻されます。ただし、Oracle XML DBデータが表形式で格納されている場合、ヌル文字で終了するのではなく、長さがエンコードされます。したがって、XDKでは、頻度の高い場合にパフォーマンスを向上させるために、長さエンコードAPIの追加セットが提供されます。特に、表18-5のDOM関数には2つのAPIがあります。

表18-5 ヌル文字終了および長さエンコードのC API関数

ヌル文字終了API 長さエンコードAPI

XmlDomGetNodeName()

XmlDomGetNodeNameLen()

XmlDomGetNodeLocal()

XmlDomGetNodeLocalLen()

XmlDomGetNodeURI()

XmlDomGetNodeURILen()

XmlDomGetNodeValue()

XmlDomGetNodeValueLen()

XmlDomGetAttrName()

XmlDomGetAttrNameLen()

XmlDomGetAttrLocal()

XmlDomGetAttrLocalLen()

XmlDomGetAttrURI()

XmlDomGetAttrURILen()

XmlDomGetAttrValue()

XmlDomGetAttrValueLen()


C APIを使用したエラー処理

C API関数では、通常、数値のエラー・コード(成功の場合は0(ゼロ)、失敗の場合は0(ゼロ)以外)が戻されるか、または変数によりエラー・コードが戻されます。いずれの場合も、APIによってエラー・コードが格納されます。アプリケーションでは、XmlDomGetLastError()関数をコールして、最新のエラーを取得できます。

デフォルトでは、関数出力エラー・メッセージはstderrに出力されます。ただし、初期化の際にエラー・メッセージ・コールバックを登録できます。エラーが発生すると、登録済コールバックがコールされ、エラーは出力されません。

SAX API for Cの使用

SAXを使用するには、xmlsaxcb構造を関数ポインタで初期化し、XmlLoadSax()に渡します。また、ユーザー定義コンテキスト構造へのポインタを含むことができます。このポインタは各SAX関数に渡されます。


関連項目:

SAXコールバック構造については、『Oracle Database XML C APIリファレンス』を参照してください。

XML Pull Parser for Cの概要

XML Pull Parserは、XMLイベント・インタフェースを実装しています。

XML Pull ParserとSAXパーサーは類似していますが、Pull Parserを使用する場合はアプリケーション(消費者)がイベントを駆動する一方、SAXパーサーの場合はパーサー(生産者)がイベントを駆動します。XML Pull ParserとSAXのいずれも、開始タグ、終了タグおよびコメントを使用して文書を一連のイベントとして表示します。XML Pull Parserは単純な一連のAPIおよび基礎となる一連のイベントを表示することでアプリケーションに制御を渡します。SAXなどのコールバック内でイベントを処理するのではなく、XmlEvNextのようなメソッドを使用して、アプリケーションは次のイベントをリクエストしたり、または取り出すことができます。そのため、アプリケーションではXML処理においてより手続き的な制御を行います。また、文書全体を解析するSAXアプリケーションとは異なり、処理を途中で停止することもできます。

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

基本的なXML Pull Parsing機能の使用

XML Pull Parserを使用するには、示される順序に従って、次の操作を行いません。

  1. XmlCreateをコールしてXMLメタコンテキストを初期化します。

  2. イベント・コンテキストを作成して戻すXmlEvCreatePPCtx関数をコールしてPull Parserコンテキストを初期化します。

    XmlEvCreatePPCtx関数は、XmlLoadDomおよびXmlLoadSaxなどにサポートされるすべてのプロパティをサポートします。

    XmlEvCreatePPCtx関数およびXmlEvCreatePPCtxVA関数は完全に実装されています。

  3. イベント・コンテキストが、Pull Parserへのすべての副問合せコールに渡されることを確認します。

  4. XmlEvDestoryPPCtx関数をコールしてPull Parserコンテキストを終了し、メモリーを削除します。

  5. XmlDestoryCtx関数をコールしてXMLメタコンテキストを削除します。

XMLイベント・コンテキスト

例18-3に、イベント・コンテキストの構造を示します。

例18-3 XMLイベント・コンテキスト

typedef  struct {
   void *ctx_xmlevctx;                   /* implementation specific context */
   xmlevdisp *disp_xmlevctx;             /* dispatch table */
   ub4 checkword_xmlevctx;               /* checkword for integrity check */
   ub4 flags_xmlevctx;                   /* mode; default: expand_entity */
   struct xmlevctx *input_xmlevctx;      /* input xmlevctx; chains the XML Event
                                            context */
} xmlevctx;

XMLイベント・コンテキストの概要

各XML Pull Parserでは、固有のコンテキストの作成および固有のAPI関数の実装が可能です。

  • ディスパッチ表

    disp_xmlevctxというディスパッチ表には、XmlEvCreatePPCtx関数、XmlEvCreatePPCtxVA関数、XmlEvDestoryPPCtx関数、XmlEvLoadPPDoc関数、およびXmlEvCleanPPCtx関数を除いて、各API関数に1つのポインタが含まれます。

    イベント・コンテキストが作成されると、ポインタdisp_xmlevctxは静的表のアドレスで初期化されます。

  • 実装固有のイベント・コンテキスト

    フィールドctx_xmlevctxは、特定の実装の起動に固有のコンテキストのアドレスで初期化される必要があります。実装固有のイベント・コンテキストは*void型なので、別のアプリケーションとは異なります。

  • 入力イベント・コンテキスト

    各Pull Parserは、xmlevctxという入力イベント・コンテキストを指定できます。このフィールドでは、パーサーにより複数のイベント生産者を連鎖させることが可能です。結果として、コンテキストでディスパッチ関数がNULLと指定されている場合、アプリケーションは入力イベント・コンテキストの連鎖にある次のNULL以外のディスパッチ関数を使用します。基本的なxmlevctxでは、すべてのディスパッチ関数のポインタはNULL以外であることを確認する必要があります。

複数のXML文書の解析

XMLイベント・コンテキストを作成して初期化した後、XmlEvLoadPPDocおよびXmlEvCleanPPCtxに対して繰り返されたコールを使用して複数の文書を解析できます。これらの関数は完全に実装されています。

XMLイベント・コンテキスト作成中にアプリケーションによって定義されたプロパティは、XmlLoadPPDoc関数への各コールに対して変更できないことに注意してください。プロパティを変更する場合は、イベント・コンテキストを削除して再作成します。

XmlEvCleanPPCtxにより現行のパーサーの内部構造を削除した後、イベント・コンテキストを再利用して別の文書を分析できます。

IDコールバック

コールバックを提供して、テキストベースの名前から8バイトのIDに変換できます。

コールバック関数署名

typedef  sb8 (*xmlev_id_cb_funcp)( void *ctx , ub1 type, ub1 *token, ub4 tok_len,
              sb8 nmspid, boolean isAttribute);

戻り値

sb8は8バイトのIDです。

引数

  • *ctxはコンテキスト実装です。

  • typeは次の列挙に含まれる型です。

    typedef enum
    {
      XML_EVENT_ID_URI,
      XML_EVENT_ID_QNAME,
    }xmlevidtype;
    
  • *tokenおよびtok_lenは変換される実際のテキストです。

  • nmspidは名前空間IDです。

  • isAttributeは単一の属性を示すブール値です。

XmlEvGetTagIdおよびXmlEvGetAttrIDは内部的にこのコールバックを2回コールし、1回は名前空間IDをフェッチし、他の1回はタグの実際のIDまたはQname属性をフェッチします。

XmlEvGetTagUriID関数およびXmlEvGetAttrUriID関数によりこのコールバックを1回起動して対応するURIのIDを取得します。

コールバックが指定されない場合、これらのAPIが使用されるとエラーXML_ERR_EVENT_NOIDCBKが戻されます。

XML Pull Parserのエラー処理

次の項では、XML Pull Parserのエラー処理について説明します。

パーサー・エラー

入力文書が不正な形式であるためエラーがスローされると、XML Pull ParserによりXML_EVENT_FATAL_ERRORというメッセージが戻されます。XmlEvGetError関数によりエラー番号およびメッセージが取得されます。

XmlCreate中にアプリケーションによって指定されたエラー処理は、XmlEvCreatePPCtx操作の間、オーバーライドされることに注意してください。XmlEvDestroyPPCtx操作で元のコールバックをリストアした後、アプリケーションはXmlErrSetHandler関数をコールする必要があります。

プログラミング・エラー

プログラムのエラーを処理するため、XDKには、アプリケーションがイベント・コンテキスト作成時に指定できるコールバックが用意されています。このコールバックは、アプリケーションが無効なAPIにコールを作成する場合に起動されます。コールバック署名は次のとおりです。

typedef  void (* xmlev_err_cb_funcp)(xmlctx *xctx, xmlevctx *evctx,
        xmlevtype cur_event);

無効なAPIコールの例は次のとおりです。

XmlEvGetNameXML_EVENT_CHARACTERSイベントに対してコールされません。

サンプルPull Parserアプリケーション

この項では、サンプルPull Parserアプリケーション、解析する文書、および文書から生成するイベントのリストについて説明します。

XML Pull Parserサンプル・アプリケーション

例18-4 サンプルPull Parserアプリケーションの例

# include "xml.h"
# include "xmlev.h"
...
xmlctx *xctx;
xmlevctx *evtcx;
if (!(xctx = XmlCreate(&xerr, (oratext *) "test")))
{
    printf("Failed to create XML context, error %u\n", (unsigned) xerr);
    return -1;
}
...
if(!(evctx = XmlEvCreatePPCtx(xctx, &xerr, NULL)))
{
   printf("Failed to create EVENT context, error %u\n", (unsigned) xerr);
   return -1;
 }
for(i = 0; i < numDocs; i++)
{
  if (xerr = XmlEvLoadPPDoc(xctx, evctx, "file", input_filenames[i], 0, NULL)
     {
       printf("Failed to load the document, error %u\n", (unsigned) xerr);
       return -1;
     }
...
  for(;;)
  {
    xmlevtype cur_event;
    cur_event = XmlEvNext(evctx);
    switch(cur_event)
         {
               case XML_EVENT_FATAL_ERROR:
                     XmlEvGetError(evctx, (oratext **)&errmsg);
                          printf("Error %s\n", errmsg);
               return;
               case XML_EVENT_START_ELEMENT:
                     printf("<%s>", XmlEvGetName0(evctx));
               break;
               case XML_EVENT_END_DOCUMENT:
                     printf("<%s>", XmlEvGetName0(evctx));
               return;
         }
  }
  XmlEvCleanPPCtx(xctx, evctx);
}
XmlEvDestroyPPCtx(xctx, evctx);
XmlDestroy(xctx);

サンプル文書

例18-5 解析するサンプル文書

<!DOCTYPE doc [
<!ENTITY ent SYSTEM "file:attendees.txt">
<!ELEMENT doc ANY>
<!ELEMENT meeting (topic, date, publishAttendees)>
<!ELEMENT publishAttendees (#PCDATA)>
<!ELEMENT topic (#PCDATA)>
<!ELEMENT date (#PCDATA)>
]>
<!-- Begin Document -->
<doc>
  <!-- Info about the meeting -->
  <meeting>
    <topic>Group meeting</topic>
    <date>April 25, 2005</date>
    <publishAttendees>&ent;</publishAttendees>
  </meeting>
</doc>
<!-- End Document -->

XML Pull Parserサンプル・アプリケーションにより生成されたイベント

これは、属性イベント・プロパティがFALSEで展開エンティティ・プロパティがTRUEである場合に生成される一連のイベントです。

例18-6 サンプル文書の解析により生成されたイベント

XML_EVENT_START_DOCUMENT
XML_EVENT_START_DTD
XML_EVENT_PE_DECLARATION
XML_EVENT_ELEMENT_DECLARATION
XML_EVENT_ELEMENT_DECLARATION
XML_EVENT_ELEMENT_DECLARATION
XML_EVENT_ELEMENT_DECLARATION
XML_EVENT_ELEMENT_DECLARATION
XML_EVENT_END_DTD
XML_EVENT_COMMENT
XML_EVENT_START_ELEMENT
XML_EVENT_SPACE
XML_EVENT_COMMENT
XML_EVENT_SPACE
XML_EVENT_START_ELEMENT
XML_EVENT_START_ELEMENT
XML_EVENT_CHARACTERS
XML_EVENT_END_ELEMENT
XML_EVENT_START_ELEMENT
XML_EVENT_CHARACTERS
XML_EVENT_END_ELEMENT
XML_EVENT_START_ELEMENT
XML_EVENT_START_ENTITY
XML_EVENT_CHARACTERS
XML_EVENT_END_ENTITY
XML_EVENT_END_ELEMENT
XML_EVENT_END_ELEMENT
XML_EVENT_SPACE
XML_EVENT_END_ELEMENT
XML_EVENT_COMMENT
XML_EVENT_END_DOCUMENT

OCIおよびXDK C APIの使用

この項では、Oracle Call Interface(OCI)からのXDK C関数のコールについて説明します。

XMLType関数の使用および説明

データベースのXMLType列のXMLにC APIを使用できます。OCIプログラムでは、次のようなOCIハンドルの値を初期化することで、テーブルに格納されたXMLデータにアクセスできます。

  • 環境ハンドル

  • サービス・ハンドル

  • エラー・ハンドル

  • オプションのパラメータ

プログラムでは、これらの入力値をOCIXmlDbInitXmlCtx()関数に渡すことができます。この関数はXMLコンテキストを戻します。プログラムでは、C APIをコールした後、OCIXmlDbFreeXmlCtx()関数によってコンテキストを解放します。

表18-6に、XML操作に使用する関数の一部を示します。

表18-6 XMLType関数

関数名 説明

XmlCreateDocument()

空のXMLTypeインスタンスの作成

XmlLoadDom()など

ソース・バッファからの作成

XmlXPathEvalexpr()およびファミリ

XPath式の抽出

XmlXslProcess()およびファミリ

XSLTスタイルシートを使用した変換

XmlXPathEvalexpr()およびファミリ

XPathの有無のチェック

XmlDomIsSchemaBased()

文書がスキーマベースかどうかの確認

XmlDomGetSchema()

スキーマ情報の取得

XmlDomGetNodeURI()

文書の名前空間の取得

XmlSchemaValidate()

スキーマを使用した検証

(void *)(xmldocnode *)へのキャスト

XMLTypeからのDOMの取得

(xmldocnode *)(void *)へのキャスト

DOMからのXMLTypeの取得


XML DBに対するXMLコンテキストの初期化

XMLコンテキストは、すべてのC DOM API関数における必須パラメータです。この不透明なコンテキストは、データ・エンコーディング、エラー・メッセージ言語などに関する情報をカプセル化します。XMLコンテキストの内容は、XDKアプリケーションとOracle XML DBアプリケーションでは異なります。


注意:

XML DBアプリケーションでXDK用のXMLコンテキストを使用したり、XDKアプリケーションでXML DB用のXMLコンテキストを使用しないでください。

Oracle XML DBでは、XMLコンテキストを初期化および解放する2つのOCI関数があり、プロトタイプは次のとおりです。

xmlctx *OCIXmlDbInitXmlCtx (OCIEnv *envhp, OCISvcCtx *svchp, OCIError *errhp,
       ocixmldbparam *params, ub4 num_params);

void OCIXmlDbFreeXmlCtx (xmlctx *xctx);

関連項目:

  • 関数の参考資料は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

  • XMLに対するOCIサポートの詳細は、『Oracle Call Interfaceプログラマーズ・ガイド』を参照してください。

  • DOC APIの参考情報は、『Oracle Database XML C APIリファレンス』を参照してください。


クライアント上でのXMLTypeインスタンスの作成

XmlLoadDom()コールを使用して、クライアントに新しいXMLTypeインスタンスを構築できます。次の基本手順に従います。

  1. 「DOM API for Cの使用」の例に示されているように、最初にxmlctxを初期化する必要があります。

  2. 次のソースからXMLデータ自体を構築できます。

    • ユーザー・バッファ

    • ローカル・ファイル

    • URI

    これらの場所からの戻り値は(xmldocnode *)であり、残りの共通C APIで使用できます。

  3. 最後に、(xmldocnode *)を(void *)にキャストし、必要に応じて直接バインド値として提供できます。

XmlCreateDocument()コールを使用して、空のXMLTypeインスタンスを構築できます。この関数は、他のタイプに対するOCIObjectNew()に相当します。前のコールで戻された(xmldocnode *)について操作し、バインド値として提供される必要がある場合、最後にこれを(void *)にキャストできます。

データベース・サーバーのXMLデータに対する操作

Oracle Database上のXMLデータは、OCI文のコールによって操作できます。xmldocnodeを使用してXMLType値をバインドして定義し、OCI文を使用してデータベースからXMLデータを選択できます。このデータは、C DOM関数で直接使用できます。同様に、SQL文に直接値をバインドできます。

OCIおよびXDK C APIの使用: 例

例18-7に、DOM APIを使用してスキーマベース文書を構築し、データベースに保存する方法を示します。ヘッダー・ファイルxml.hおよびocixmldb.hをインクルードする必要があることに注意してください。

例18-7 DOM APIを使用したスキーマベース文書の構築

#include <xml.h>
#include <ocixmldb.h>
static oratext tlpxml_test_sch[] = "<TOP xmlns='example1.xsd'\n\
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' \n\
xsi:schemaLocation='example1.xsd example1.xsd'/>";

void example1()
{
    OCIEnv *envhp;
    OCIError *errhp;
    OCISvcCtx *svchp;
    OCIStmt *stmthp;
    OCIDuration dur;
    OCIType *xmltdo;

    xmldocnode  *doc;
    ocixmldbparam params[1];
    xmlnode *quux, *foo, *foo_data;
    xmlerr       err;

    /* Initialize envhp, svchp, errhp, dur, stmthp */
    /* ........ */

    /* Get an xml context */
    params[0].name_ocixmldbparam = XCTXINIT_OCIDUR;
    params[0].value_ocixmldbparam = &dur;
    xctx = OCIXmlDbInitXmlCtx(envhp, svchp, errhp, params, 1);

    /* Start processing */
    printf("Supports XML 1.0: %s\n",
       XmlHasFeature(xctx, (oratext *) "xml", (oratext *) "1.0") ? "YES" : "NO");

    /* Parsing a schema-based document */
    if (!(doc = XmlLoadDom(xctx, &err, "buffer", tlpxml_test_sch,
                          "buffer_length", sizeof(tlpxml_test_sch)-1,
                          "validate", TRUE, NULL)))
    {
       printf("Parse failed, code %d\n");
       return;
    }

    /* Create some elements and add them to the document */
    top = XmlDomGetDocElem(xctx, doc);
    quux = (xmlnode *) XmlDomCreateElem(xctx ,doc, (oratext *) "QUUX");
    foo = (xmlnode *) XmlDomCreateElem(xctx, doc, (oratext *) "FOO");
    foo_data = (xmlnode *) XmlDomCreateText(xctx, doc, (oratext *)"foo's data");
    foo_data = XmlDomAppendChild(xctx, (xmlnode *) foo, (xmlnode *) foo_data);
    foo = XmlDomAppendChild(xctx, quux, foo);
    quux = XmlDomAppendChild(xctx, top, quux);

    XmlSaveDom(xctx, &err, top, "stdio", stdout, NULL);
    XmlSaveDom(xctx, &err, doc, "stdio", stdout, NULL);

    /* Insert the document to my_table */
    ins_stmt = "insert into my_table values (:1)";

    status = OCITypeByName(envhp, errhp, svchp, (const text *) "SYS",
                   (ub4) strlen((char *)"SYS"), (const text *) "XMLTYPE",
                   (ub4) strlen((char *)"XMLTYPE"), (CONST text *) 0,
                   (ub4) 0, dur, OCI_TYPEGET_HEADER,
                   (OCIType **) &xmltdo)) ;

    if (status == OCI_SUCCESS)
    {
       exec_bind_xml(svchp, errhp, stmthp, (void *)doc, xmltdo, ins_stmt));
    }

   /* free xml ctx */
   OCIXmlDbFreeXmlCtx(xctx);
}

/*--------------------------------------------------------*/
/* execute a sql statement which binds xml data */
/*--------------------------------------------------------*/
sword exec_bind_xml(svchp, errhp, stmthp, xml, xmltdo, sqlstmt)
OCISvcCtx *svchp;
OCIError *errhp;
OCIStmt *stmthp;
void *xml;
OCIType *xmltdo;
OraText *sqlstmt;
{
   OCIBind *bndhp1 = (OCIBind *) 0;
   OCIBind *bndhp2 = (OCIBind *) 0;
   sword  status = 0;
   OCIInd ind = OCI_IND_NOTNULL;
   OCIInd *indp = &ind;

   if(status = OCIStmtPrepare(stmthp, errhp, (OraText *)sqlstmt,
                    (ub4)strlen((char *)sqlstmt),
                    (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)) {
     return OCI_ERROR;
   }

   if(status = OCIBindByPos(stmthp, &bndhp1, errhp, (ub4) 1, (dvoid *) 0,
                   (sb4) 0, SQLT_NTY, (dvoid *) 0, (ub2 *)0,
                   (ub2 *)0, (ub4) 0, (ub4 *) 0, (ub4) OCI_DEFAULT)) {
     return OCI_ERROR;
   }

   if(status = OCIBindObject(bndhp1, errhp, (CONST OCIType *) xmltdo,
               (dvoid **) &xml, (ub4 *) 0, (dvoid **) &indp, (ub4 *) 0)) {
     return OCI_ERROR;
   }

   if(status = OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT)) {
     return OCI_ERROR;
  }

   return OCI_SUCCESS;
}

例18-8に、DOM APIを使用してデータベースから文書を取得し、変更する方法を示します。

例18-8 DOM APIを使用したデータベース文書の変更

#include <xml.h>
#include <ocixmldb.h>
sword example2()
{
    OCIEnv *envhp;
    OCIError *errhp;
    OCISvcCtx *svchp;
    OCIStmt *stmthp;
    OCIDuration dur;
    OCIType *xmltdo;

    xmldocnode  *doc;
    xmlnodelist *item_list; ub4 ilist_l;
    ocixmldbparam params[1];
    text *sel_xml_stmt = (text *)"SELECT xml_col FROM my_table";
    ub4    xmlsize = 0;
    sword  status = 0;
    OCIDefine *defnp = (OCIDefine *) 0;

    /* Initialize envhp, svchp, errhp, dur, stmthp */
    /* ... */

    /* Get an xml context */
    params[0].name_ocixmldbparam = XCTXINIT_OCIDUR;
    params[0].value_ocixmldbparam = &dur;
    xctx = OCIXmlDbInitXmlCtx(envhp, svchp, errhp, params, 1);

    /* Start processing */
    if(status = OCITypeByName(envhp, errhp, svchp, (const text *) "SYS",
                   (ub4) strlen((char *)"SYS"), (const text *) "XMLTYPE",
                   (ub4) strlen((char *)"XMLTYPE"), (CONST text *) 0,
                   (ub4) 0, dur, OCI_TYPEGET_HEADER,
                   (OCIType **) xmltdo_p)) {
       return OCI_ERROR;
    }

    if(!(*xmltdo_p)) {
       printf("NULL tdo returned\n");
       return OCI_ERROR;
    }

    if(status = OCIStmtPrepare(stmthp, errhp, (OraText *)selstmt,
                    (ub4)strlen((char *)selstmt),
                    (ub4) OCI_NTV_SYNTAX, (ub4) OCI_DEFAULT)) {
      return OCI_ERROR;
    }

    if(status = OCIDefineByPos(stmthp, &defnp, errhp, (ub4) 1, (dvoid *) 0,
                   (sb4) 0, SQLT_NTY, (dvoid *) 0, (ub2 *)0,
                   (ub2 *)0, (ub4) OCI_DEFAULT)) {
       return OCI_ERROR;
    }

    if(status = OCIDefineObject(defnp, errhp, (OCIType *) *xmltdo_p,
                            (dvoid **) &doc,
                            &xmlsize, (dvoid **) 0, (ub4 *) 0)) {
      return OCI_ERROR;
    }

    if(status = OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0,
                 (CONST OCISnapshot*) 0, (OCISnapshot*) 0, (ub4) OCI_DEFAULT)) {
      return OCI_ERROR;
    }

    /* We have the doc. Now we can operate on it */
    printf("Getting Item list...\n");

   item_list = XmlDomGetElemsByTag(xctx,(xmlelemnode *) elem,(oratext *)"Item");
    ilist_l   = XmlDomGetNodeListLength(xctx, item_list);
    printf(" Item list length = %d \n", ilist_l);

    for (i = 0; i < ilist_l; i++)
    {
      elem = XmlDomGetNodeListItem(xctx, item_list, i);
      printf("Elem Name:%s\n", XmlDomGetNodeName(xctx, fragelem));
      XmlDomRemoveChild(xctx, fragelem);
    }

    XmlSaveDom(xctx, &err, doc, "stdio", stdout, NULL);

   /* free xml ctx */
   OCIXmlDbFreeXmlCtx(xctx);

   return OCI_SUCCESS;
}