この章では、Extensible Markup Language (XML) Parser for Cの使用方法について説明します。
内容は次のとおりです。
内容は次のとおりです。
関連項目:
Document Object Model (DOM)およびSimple API for XML (SAX)を使用したXML解析の概要は、「XML Parsing for Javaの概要」を参照してください。概要の大部分の情報は特定の言語に依存しておらず、Cにも適用されます。
Oracle XML Parser for CはXML文書を読み取り、DOMまたはSAXアプリケーション・プログラミング・インタフェース(API)を使用してそのコンテンツと構造へのプログラムでのアクセスを提供します。このパーサーは検証モードまたは非検証モードで使用できます。またPull Parserも使用可能です。
この章では、次のテクノロジを十分に理解していると想定します。
Document Object Model (DOM): DOMは、XML文書の構造を示すインメモリー・ツリー表現です。
Simple API for XML (SAX): SAXは、イベントベースのXML解析の標準です。
XML Pull Parser for Cの使用Pull ParserではXMLイベントが使用されます。
Document Type Definition (DTD): XML DTDは、XML文書の有効な構造を定義します。
XMLスキーマ: DTDと同様にXMLスキーマはXML文書の有効な構造を定義します。
XML名前空間: 名前空間は、要素名や属性名を区別するためのメカニズムです。
前述のテクノロジの概要説明が必要な場合は、「関連ドキュメント」にあるXMLの資料を参照してください。
XML 1.0はW3C勧告です。Oracle XML Developer's Kit (XDK) for C APIはXML 1.0(Second Edition)をフル・サポートしています。Second Editionの仕様は次の場所にあります。
http://www.w3.org/TR/2000/REC-xml-20001006
DOMレベル1、レベル2およびレベル3の仕様はWorld Wide Web Consortium (W3C)勧告です。XDK for C APIはDOMレベル1および2をフル・サポートしていますが、レベル3はサポートしていません。次の場所に、3つのレベルすべての仕様へのリンクがあります。
http://www.w3.org/DOM/DOMTR
SAXにはバージョン1.0と2.0があり、バージョン1.0は非推奨です。SAXはW3C仕様ではありません。XDK for C APIはSAX 1.0および2.0の両方をフル・サポートしています。SAXのドキュメントは次の場所にあります。
http://www.saxproject.org
XML名前空間はW3C勧告です。仕様は次の場所にあります。
http://www.w3.org/TR/REC-xml-names
関連項目:
XDKでサポートする標準の概要は、「Oracle XML Developer's Kit標準」を参照してください
Oracle XML Parser for Cは、XML文書が整形式であるかどうか、およびDTDに対して妥当であるかどうか(オプション)を確認します。アプリケーションでは、DOMまたはSAX APIを使用して、解析されたデータにアクセスできます。
XML解析APIの中核は、XML、DOM、SAXの各APIです。表20-1に、これらのAPIのインタフェースを示します。APIドキュメントの詳細は、『Oracle Database XML C APIリファレンス』を参照してください。
表20-1 XML、DOM、SAXの各APIのインタフェース
| パッケージ | インタフェース | 関数名の表記規則 |
|---|---|---|
XML |
このパッケージは、1つの
|
関数名は文字列 APIドキュメントについては、『Oracle Database XML C APIリファレンス』を参照してください。 |
DOM |
このパッケージは、解析済XMLへのプログラム・アクセスを提供します。このパッケージは次のインタフェースを実装します。
|
関数名は文字列 APIドキュメントについては、『Oracle Database XML C APIリファレンス』を参照してください。 |
SAX |
このパッケージは、解析済XMLへのプログラム・アクセスを提供します。このパッケージは、SAXイベントの通知を受け取る関数を定義する |
関数名は文字列 APIドキュメントについては、『Oracle Database XML C APIリファレンス』を参照してください。 |
XML Pull Parser |
XMLイベントとはXML文書の表現で、開始タグ、終了タグ、コメントなどのような一連のイベントで文書が表現されるSAXイベントに類似しています。SAXイベントはパーサー(生産者)によって駆動され、XMLイベントはアプリケーション(消費者)によって駆動されるという点で異なります。 |
関数名は文字列 APIドキュメントについては、『Oracle Database XML C APIリファレンス』を参照してください。 |
XDK for Cのデータ型の完全なリストは、Oracle Database XML C APIリファレンスを参照してください。表20-2に、XML Parser for Cで使用するデータ型を示します。
表20-2 XML Parser for Cで使用するデータ型
| データ型 | 説明 |
|---|---|
|
文字列ポインタ |
|
マスターXMLのコンテキスト |
|
SAXコールバック構造(SAXのみ) |
|
32ビット以上の符号なし整数 |
|
ネイティブな符号なし整数 |
XML Parser for Cのデフォルトは、次のとおりです。
キャラクタ・セット・エンコーディングは8ビット・エンコーディングのUnicode (UTF-8)です。ドキュメントがすべてASCII形式である場合は、パフォーマンス向上のために、エンコーディングをUS-ASCIIに設定することをお薦めします。
パーサーのメッセージは、エラー・ハンドラが提供されていないかぎり、stderrに出力されます。
パーサーは入力文書が整形式であるかどうかを確認しますが、妥当であるかどうかは確認しません。プロパティ"validate"を設定することで、入力の妥当性を検証できます。
注意:
シングルバイト・キャラクタ・セット(US-ASCII、ISO 8859キャラクタ・セットのいずれかなど)のみを使用している場合、明示的にデフォルトのエンコーディングを設定することをお薦めします。UTF-8などのマルチバイト・キャラクタ・セットを使用した場合よりパフォーマンスが向上します。
空白を処理する際、パーサーはXML 1.0仕様に準拠します。つまり、パーサーは空白をすべてアプリケーションに報告しますが、無視できる空白を示します。ただし、アプリケーションによっては空白文字の破棄プロパティを設定し、要素の終了タグと次の要素の開始タグの間のすべての空白を破棄する場合もあります。
関連項目:
DOM、SAX、Pull ParserおよびコールバックのAPIの詳細は、『Oracle Database XML C APIリファレンス』を参照してください。
http://www.oracle.com/technetwork/database-features/xdk/overview/index.html
アプリケーションで次の手順を実行します。
独自のメモリー割当てにはメモリー・コールバック関数XML_ALLOC_FおよびXML_FREE_Fを使用できます。その場合、両方の関数を指定します。
$ORACLE_HOME/xdk/demo/c/(UNIXの場合)および%ORACLE_HOME%\xdk\demo\c(Windowsの場合)ディレクトリには、DOMインタフェースおよびSAXインタフェースによるXML Parser for Cの使用方法を示すXMLアプリケーションがあります。表20-3に、デモを示します。
makeユーティリティでは、ソース・ファイルfileName.cをコンパイルし、デモ・プログラムfileNameおよび出力ファイルfileName.outを作成できます。予期される出力は、fileName.stdです。
表20-3 Cパーサーのデモ
| ディレクトリ | 内容 | デモ |
|---|---|---|
|
DOMNamespace.c DOMSample.c FullDom.c FullDom.xml NSExample.xml Traverse.c XPointer.c class.xml cleo.xml pantry.xml |
次のデモ・プログラムではDOM APIを使用します。
|
|
NSExample.xml SAXNamespace.c SAXSample.c cleo.xml |
次のデモ・プログラムではSAX APIを使用します。
|
サンプル・プログラムのコンパイルおよび実行方法を説明するドキュメントは、同じディレクトリ内のREADMEにあります。この場合、基本的な手順は次のようになります。
ディレクトリを$ORACLE_HOME/xdk/demo/c(UNIXの場合)または%ORACLE_HOME%\xdk\demo\c(Windowsの場合)に変更します。
「UNIXでのXDK for C環境変数の設定」および「WindowsでのXDK for C環境変数の設定」の説明に従って、環境変数が設定されていることを確認します。
システム・プロンプトでmake(UNIXの場合)またはMake.bat(Windowsの場合)を実行します。makeユーティリティでは、ディレクトリを各デモ・サブディレクトリに変更し、makeを実行して次の処理を行います。
ccユーティリティでCソース・ファイルをコンパイルします。たとえば、$ORACLE_HOME/xdk/demo/c/domディレクトリのMakefileには、次の行があります。
$(CC) -o DOMSample $(INCLUDE) $@.c $(LIB)
各デモ・プログラムを実行し、出力をファイルにリダイレクトします。たとえば、$ORACLE_HOME/xdk/demo/c/domディレクトリのMakefileには、次の行があります。
./DOMSample > DOMSample.out
プログラムごとに*.stdファイルと*.outファイルを比較します。*.stdファイルには、各プログラムの予期される出力が入っています。たとえば、DOMSample.stdには、DOMSampleの実行で予期される出力が入っています。
$ORACLE_HOME/bin (UNIXの場合)または%ORACLE_HOME%\bin (Windowsの場合)にあるxmlユーティリティは、XML文書を解析するコマンドライン・インタフェースです。XML文書が整形式であるか、および妥当であるかを確認します。
xmlを使用するには、「UNIXでのXDK for C環境変数の設定」および「WindowsでのXDK for C環境変数の設定」の説明に従って、環境が設定されていることを確認します。
コマンドラインで次の構文を使用して、xmlを起動します。Windowsではxml.exeを使用します。
xml [options] [document URI] xml -f [options] [document filespec]
表20-4に、コマンドライン・オプションを示します。
表20-4 C XML Parserのコマンドライン・オプション
| オプション | 説明 |
|---|---|
|
XSLTプロセッサのベースURIを設定します。 |
|
入力文書が整形式であるかどうかを確認しますが、妥当であるかどうかは確認しません。 |
|
デフォルトの入力ファイルのエンコーディング(インコーディング)を指定します。 |
|
DOM/SAXのエンコーディング(アウトコーディング)を指定します。 |
|
ファイルをURIではなくfilespecとして解析します。 |
|
ファイルに指定された |
|
使用方法のヘルプおよびコマンドライン・オプションの基本リストを表示します。 |
|
コマンドライン・オプションの詳細なリストを表示します。 |
|
|
|
エラー報告用の言語を指定します。 |
|
DOMを検索し、要素の数を報告します。次に、出力例を示します。 ELEMENT 1
PCDATA 1
DOC 1
TOTAL 3 * 60 = 180
|
|
|
|
解析後の文書とDTDの構造を出力します。たとえば、ルート要素 +---ELEMENT greeting
+---PCDATA "hello"
|
|
ルート要素から文書を出力します。たとえば、ルート要素 <greeting>hello</greeting> |
|
ルート・ノード(DOC)からXML宣言を含めて出力します。 |
|
|
|
出力にXML宣言を含めます。 |
|
|
|
XDKパーサーのバージョンを表示した後、終了します。 |
|
|
|
空白をすべて保持します。 |
|
警告の後で解析を停止します。 |
|
SAXインタフェースを実行し、文書を出力します。次に出力例を示します。 StartDocument
XMLDECL version='1.0' encoding=FALSE
<greeting>
"hello"
</greeting>
EndDocument
|
$ORACLE_HOME/xdk/demo/cにある様々なXMLファイルに対してxmlをテストできます。例20-1に、NSExample.xmlの内容を示します。
次の例に示すように、このファイルを解析し、要素数を計算し、DOMツリーを表示できます。
xml -np NSEample.xml > xml.out
例20-2に出力を示します。
例20-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>
例20-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.
"
XMLデータは多くのエンコーディングに存在します。XMLエンコーディングは次のように制御できます。
自己記述的でないファイルに対して前提とするデフォルト・エンコーディングの指定
DOMまたはSAXの表示用エンコーディングの指定
DOMがシリアライズされている場合の再エンコーディング
入力XMLデータは常にエンコードされています。16ビット・エンコーディングのUnicode (UTF-16)など、エンコーディングによっては完全に自己記述的なものがあり、この場合は実際のデータの開始前に特定のバイト・オーダー・マーク(BOM)が必要です。XMLDeclまたは文書のMultipurpose Internet Mail Extensions (MIME)ヘッダーでもエンコーディングを指定できます。アプリケーションで特定のエンコーディングを決定できない場合、デフォルトの入力エンコーディングが適用されます。デフォルトを指定していない場合、ASCIIプラットフォームではUTF-8、EBCDICプラットフォームではUTF-EBCDICが想定されます。
APIでは、入力文書のエンコーディング・データが不正である場合に備えて、対策が取られています。たとえば、encoding=asciiと記述されているXMLDeclが含まれるASCII文書がEBCDICに変換されたとします。新しいEBCDIC文書には、(EBCDIC内に)文書がASCIIであると誤って主張するXMLDeclが含まれます。XMLデータを再エンコードするプログラムの正しい動作は、XMLDeclの変換ではなく、これを再生成することです。XMLDeclは、データそのものではなく、メタデータです。しかし、このルールは無視されることが多く、そのため不正な文書が生成されます。この問題を回避するため、APIには入力エンコーディングの強制的な設定を可能にする追加フラグが提供されており、不正なXMLDeclの問題が解決されています。
入力エンコーディングを決定するための優先順位ルールは、次のとおりです。
アプリケーションでは、入力エンコーディングが決定された後、ドキュメントの解析およびデータの表示が可能になります。表示用エンコーディングを選択できます。元の入力エンコーディングにかかわらず、データにはこの表示用エンコーディングが実行されます。
アプリケーションでシリアライズ形式でDOMを書き戻す時点で、表示用データの再エンコードを選択できます。したがって、シリアライズされた文書を任意のエンコーディングにすることができます。
Cにおけるネイティブな文字列表現はNULL文字で終了します。そのため、プライマリDOMインタフェースではNULL文字で終了する文字列が使用され、戻されます。ただし、Oracle XML DB dataデータが表形式で格納されている場合、NULLで終了するのではなく、長さがエンコードされます。したがって、XDKでは、頻度の高い場合にパフォーマンスを向上させるために、長さエンコードAPIの追加セットが提供されます。特に、表20-5のDOM関数には2つのAPIがあります。
表20-5 NULL文字終了および長さエンコードのC API関数
| NULL文字終了API | 長さエンコードAPI |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
orastream関数APIは、1つのピースですべて取得するかわりに、大規模なチャンクのデータをノードからストリームするインタフェースです。したがって、64KBより大きいノードにアクセスできます。
orastream APIは、汎用入力または出力ストリームを表します。XDKユーザーはxml.hを介して、このインタフェースを使用します。これは、このインタフェースを実装するorastreamデータ構造および関数のセットにより定義されます。ストリームの作成者は、ストリーム関数アドレスのリストおよびストリーム・コンテキストをOraStreamInitに渡します。この関数は、orastream構造のインスタンスを戻します。
初期化の時点で、複数のストリーム・プロパティを指定します。読取りまたは書込みが指定された場合、ストリームはOraStreamRead()およびOraStreamWrite()を使用してバイト・モードで動作します。"read_char"または"write_char"が指定された場合、ストリームはOraStreamReadChar()およびOraStreamWriteChar()を使用してキャラクタ・モードで動作します。キャラクタ・モードの場合、完全な文字のみを読み込みまたは書き込み、バッファ境界を越えて分割しません。
ストリーム・コンテキストは、orastreamの状態を表すのに使用され、ストリームの存続期間中、存続します。
Javaの入力または出力ストリームと同様、常に、データのソースまたはシンクを指定します。出力ストリームはデータを移入する外部ストリームまたはオブジェクトのアドレスを格納します。同様に、入力ストリームは読み込まれるオブジェクトのアドレスを格納します。
次に、orastream関数の例を示します。
struct orastream; typedef struct orastream orastream; typedef ub4 oraerr; /* Error code: zero is success, non-zero is failure */
/* Initialize (Create) & Destroy (Terminate) stream object */
orastream *OraStreamInit(void *sctx, void *sid, oraerr *err, ...);
oraerr OraStreamTerm(orastream *stream);
/* Set or Change SID (streamID) for stream (returns old stream ID through osid)*/
oraerr OraStreamSid(orastream *stream, void *sid, void **osid);
/* Is a stream readable or writable? */
boolean OraStreamReadable(orastream *stream);
boolean OraStreamWritable(orastream *stream);
/* Open & Close stream */
oraerr OraStreamOpen(orastream *stream, ubig_ora *length);
oraerr OraStreamClose(orastream *stream);
/* Read | Write byte stream */
oraerr OraStreamRead(orastream *stream, oratext *dest, ubig_ora size,
oratext **start, ubig_ora *nread, ub1 *eoi);
oraerr OraStreamWrite(orastream *stream, oratext *src, ubig_ora size,
ubig_ora *nwrote);
/* Read | Write char stream */
oraerr OraStreamReadChar(orastream *stream, oratext *dest, ubig_ora size,
oratext **start, ubig_ora *nread, ub1 *eoi);
oraerr OraStreamWriteChar(orastream *stream, oratext *src, ubig_ora size,
ubig_ora *nwrote);
/* Return handles for stream */
orastreamhdl *OraStreamHandle(orastream *stream);
/* Returns status: if the stream object is currently opened or not */
boolean OraStreamIsOpen(orastream *stream);
ストリーム・エラー・コードは次のとおりです。
#define ORASTREAM_ERR_NULL_POINTER 1 /* NULL pointer given */ #define ORASTREAM_ERR_BAD_STREAM 2 /* invalid stream object */ #define ORASTREAM_ERR_WRONG_DECTION 3 /* tried wrong-direction I/O */ #define ORASTREAM_ERR_UNKNOWN_PROPERTY 4 /* unknown creation prop */ #define ORASTREAM_ERR_NO_DIRECTION 5 /* neither read nor write? */ #define ORASTREAM_ERR_BI_DIRECTION 6 /* both read any write? */ #define ORASTREAM_ERR_NOT_OPEN 7 /* stream not open */ #define ORASTREAM_ERR_WRONG_MODE 8 /* wrote byte/char mode */ /* --- Open errors --- */ #define ORASTREAM_ERR_CANT_OPEN 10 /* can't open stream */ /* --- Close errors --- */ #define ORASTREAM_ERR_CANT_CLOSE 20 /* can't close stream */
関連項目:
orastream APIのパラメータ定義などの参考情報は、『Oracle Database XML C APIリファレンス』を参照してください。
例20-3 orastream関数の使用
int test_read()
{
xmlctx *xctx = NULL;
oratext *barray, *docName = "NSExample.xml";
orastream* ostream = (orastream *) 0;
xmlerr ecode = 0;
ub4 wcount = 0;
ubig_ora destsize, nread;
oraerr oerr = 0;
ub1 eoi = 0;
nread = destsize = 1024;
if (!(xctx = XmlCreateNew(&ecode, (oratext *)"stream_xctx", NULL, wcount,
NULL)))
{
printf("Failed to create XML context, error %u\n", (unsigned)ecode);
return -1;
}
barray = XmlAlloc(xctx, sizeof(oratext) * destsize);
/* open function should be specified in order to read correctly. */
if (!(ostream = OraStreamInit(NULL,docName, (oraerr *)&ecode,
"open", fileopen,
"read", fileread,
NULL)))
{
printf("Failed to initialize OrsStream, error %u\n",(unsigned)ecode);
return -1;
}
/* check readable and writable */
if (OraStreamReadable(ostream))
printf("ostream is readable\n");
else
printf("ostream is not readable\n");
if (OraStreamWritable(ostream))
printf("ostream is writable\n");
else
printf("ostream is not writable\n");
if (oerr = OraStreamRead(ostream, barray, destsize, &barray, &nread, &eoi))
{
printf("Failed to read due to orastream was not open, error %u\n", oerr);
}
/* open orastream */
OraStreamOpen(ostream, NULL);
/* read document */
OraStreamRead(ostream, barray, destsize, &barray, &nread, &eoi);
OraStreamTerm(ostream);
XmlDestroy(xctx);
return 0;
}
ORASTREAM_OPEN_F(fileopen, sctx, sid, hdl, length)
{
FILE *fh = NULL;
printf("Opening orastream %s...\n", (oratext *)sid);
if (sid && ((fh= fopen(sid, "r")) != NULL))
{
printf("Opening orastream %s...\n", (oratext *)sid);
}
else
{
printf("Failed to open input file.\n");
return -1;
}
/* store file handle generically, NULL means stdout */
hdl->ptr_orastreamhdl = fh;
return XMLERR_OK;
}
ORASTREAM_READ_F(fileread, sctx, sid, hdl,
dest, size, start, nread, eoi)
{
FILE *fh = NULL;
int i =0;
printf("Reading orastream %s ...\n", (oratext *)sid);
// read data from file to dest
if ((fh = (FILE *) hdl->ptr_orastreamhdl) != NULL)
*nread = fread(dest, 1, size, fh);
printf("Read %d bytes from orastream...\n", (int) *nread);
*eoi = (*nread < size);
if (start)
*start = dest;
printf("printing document ...\n");
for(i =0; i < *nread; i++)
printf("%c", (char)dest[i]);
printf("\nend ...\n");
return ORAERR_OK;
}
SAXを使用するには、xmlsaxcb構造を関数ポインタで初期化し、XmlLoadSax()に渡します。また、各SAX関数に渡す、ユーザー定義コンテキスト構造へのポインタを含めることができます。
関連項目:
SAXコールバック構造については、『Oracle Database XML C APIリファレンス』を参照してください。
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 Parserを使用するには、アプリケーションで次の手順を実行する必要があります。
例20-4に、イベント・コンテキストの構造を示します。
例20-4 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 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イベント・コンテキストを作成して初期化した後、XmlEvLoadPPDocおよびXmlEvCleanPPCtxを繰り返し起動し、複数の文書を解析できます。これらの関数は完全に実装されています。
XMLイベント・コンテキスト作成中にアプリケーションによって定義されたプロパティは、XmlLoadPPDoc関数への各起動に対して変更できません。プロパティを変更する場合は、イベント・コンテキストを破棄して再作成します。
XmlEvCleanPPCtxにより現行のパーサーの内部構造を削除した後、イベント・コンテキストを再利用して別の文書を分析できます。
コールバックを提供して、テキストベースの名前から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 APIは内部的にこのコールバックを2回起動し、1回は名前空間IDをフェッチし、他の1回はタグの実際のIDまたはQname属性をフェッチします。
XmlEvGetTagUriIDおよびXmlEvGetAttrUriID関数はこのコールバックを1回起動し、対応するUniversal Resource Identifier (URI)のIDを取得します。
コールバックが指定されない場合、これらのAPIが使用されるとエラーXML_ERR_EVENT_NOIDCBKが戻されます。
次の項では、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の起動の例は次のとおりです。
XmlEvGetNameはXML_EVENT_CHARACTERSイベントに対してコールされません。
この項では、サンプルPull Parserアプリケーション、解析する文書、および文書から生成するイベントのリストについて説明します。
例20-5に、サンプルのアプリケーション・コードを示します。
例20-6に、解析するサンプル文書を示します。
例20-7に、属性イベント・プロパティがFALSEで展開エンティティ・プロパティがTRUEである場合に生成される一連のイベントを示します。
例20-5 サンプル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);
例20-6 解析するサンプル文書
<!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 -->
例20-7 サンプル文書の解析により生成されたイベント
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
この項では、Oracle Call Interface(OCI)からのXDK for C関数へのアクセスについて説明します。
データベースのXMLType列のXMLにC APIを使用できます。Oracle Call Interface (OCI)プログラムでは、次のようなOCIハンドルの値を初期化することで、表に格納されたXMLデータにアクセスできます。
環境ハンドル
サービス・ハンドル
エラー・ハンドル
オプションのパラメータ
プログラムでは、これらの入力値をOCIXmlDbInitXmlCtx()関数に渡すことができます。この関数によりXMLコンテキストが戻されます。プログラムによりC APIが起動された後、OCIXmlDbFreeXmlCtx()関数によってコンテキストが解放されます。
表20-6に、XML操作に使用する関数の一部を示します。
表20-6 XMLType関数
| 関数名 | 説明 |
|---|---|
|
空の |
|
ソース・バッファからの作成 |
|
|
|
Extensible Stylesheet Language Transformation (XSLT)スタイルシートを使用した変換 |
|
|
|
文書がスキーマベースかどうかの確認 |
|
スキーマ情報の取得 |
|
文書の名前空間の取得 |
|
スキーマを使用した検証 |
|
|
|
DOMからの |
XMLコンテキストは、すべてのC DOM API関数における必須パラメータです。この不透明なコンテキストは、データ・エンコーディング、エラー・メッセージ言語などに関する情報をカプセル化します。XMLコンテキストの内容は、XDKアプリケーションとOracle XML DBアプリケーションでは異なります。
注意:
Oracle XML DBアプリケーションでXDK用のXMLコンテキストを使用したり、XDKアプリケーションでOracle 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プログラマーズ・ガイド』を参照してください。
DOM APIの参考情報は、『Oracle Database XML C APIリファレンス』を参照してください。
XmlLoadDom()の起動を使用して、クライアントに新しいXMLTypeインスタンスを構築できます。次の基本手順に従います。
XmlCreateDocument()を起動して、空のXMLTypeインスタンスを構築できます。この関数は、他のタイプに対するOCIObjectNew()に相当します。前の起動で戻された(xmldocnode *)について操作し、バインド値として提供される必要がある場合、最後にこれを(void *)にキャストできます。
Oracle Database上のXMLデータは、OCI文の起動で操作できます。xmldocnodeを使用してXMLType値をバインドして定義し、OCI文を使用してデータベースからXMLデータを選択できます。このデータは、C DOM関数で直接使用できます。同様に、SQL文に直接値をバインドできます。
例20-8に、DOM APIを使用してスキーマベース文書を構築し、データベースに保存する方法を示します。ヘッダー・ファイルxml.hおよびocixmldb.hをインクルードする必要があります。
例20-9に、DOM APIを使用してデータベースから文書を取得し、変更する方法を示します。
例20-8 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;
}
例20-9 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;
}