Oracle XDKに含まれているコンポーネントを使用して、2つのXML文書の内容の差分を確認し、片方のXML文書に差分(パッチ)を適用できます。
この章の内容は次のとおりです。
Oracle XmlDiff
を使用して、類似した2つのXML文書の差分を定義できます。XmlDiff
により差分を示すXdiff
インスタンス・ドキュメントが生成されます。Xdiff
インスタンス・ドキュメントは、XMLスキーマの1つであるXdiff
スキーマに準拠するXML文書です。
XmlPatch
を使用して、Xdiff
インスタンス・ドキュメントを受け取り、他の文書に変更を適用できます。これを使用して、多くのXML文書に同一の変更を適用できます。
XmlDiff
は、DOM APIの入力および出力のみを目的としてサポートされています。
XmlPatch
もまた、DOMの入力およびパッチ文書をサポートしています。
XmlDiff
およびXmlPatch
は、C APIまたはコマンドライン・ツールを介して使用でき、2つのSQL関数により表示されます。
XmlHash
C APIは、XMLツリーまたはサブツリーのハッシュ値を計算するために提供されます。2つのツリーまたはサブツリー間のハッシュ値が同数の場合、これらのツリーは非常に高い確率で同一です。
XmlDiff
を使用して、2つの入力文書を示すツリーを比較し差分を検出します。
この2つの入力文書では、同一のキャラクタ・セット・エンコーディングを使用する必要があります。Xdiff
(出力)インスタンス・ドキュメントには、入力文書のデータ・エンコーディングと同一のエンコーディングが含まれます。
比較に関して、最適化と呼ばれる2つのオプションがあります。
グローバル最適化: デフォルト
文書ツリー全体が比較されます。
ローカル最適化
兄弟関係のレベルで比較が行われます。ローカル最適化では、2つのツリーの対応する親の下にある兄弟関係が比較されます。
グローバル最適化では、大きな文書に対してより多くの時間および領域を使用できますが、生成されるのは常に最小の差分セット(最適差分)です。ローカル最適化の方が処理速度は速いですが、最適差分は生成されない場合があります。
一般的に、ハッシュによりグローバル最適化が早くなりますが、質が若干落ちる可能性もあります。ローカル最適化では、ハッシュにより差分出力の質が改善されます。異なるハッシュのレベルを使用すると、ローカル差分およびグローバル差分の両方が生成される可能性があります。
ローカルおよびグローバルの両方の最適化に関するハッシュの使用を指定できます。
ハッシュを指定するには、hashLevel
パラメータを入力します。hashLevel
が1以上の場合、DOMHash
値を使用して、差分がdepth >= hashLevel
のすべてのサブツリーを比較できます。ハッシュ値が同一の場合は、サブツリーも同一と推定されます。
XmlDiff
は、比較の実行中、属性順に差分を無視します。
XmlDiff
はDocType宣言を無視します。ファイルはDTDに対して検証されません。
名前空間の接頭辞が同一の名前空間URIを参照する場合は、XmlDiff
は名前空間の接頭辞における差分を無視します。それ以外の場合で、2つのノードに同じローカル名および内容が含まれていて名前空間URIが異なる場合は、差分が明示されます。
注意: XmlDiff は入力文書上で、スキーマに基づかない方法で動作します。要素または属性上で、型を認識する方法では動作しません。 |
表21-1に、コマンドライン・オプションを示します。
表21-1 C言語に関するコマンドライン・オプション
オプション | 説明 |
---|---|
|
デフォルトの入力ファイルのエンコーディングを指定します。XMLファイルでエンコーディングが指定されていない場合、このエンコーディングは入力対象とみなされます。 |
|
出力/データのエンコーディングを指定します。DOMおよび |
|
ハッシュのレベルを指定します。
|
|
グローバル最適化(デフォルト)を設定します。 |
|
ローカル最適化を設定します。 |
|
使用方法のヘルプを表示します。 |
|
更新操作の無効にします。 |
例21-1は、XmlDiff
およびXmlPatch
の使用による更新を説明するxml
文書のサンプルです。いくつかの変更が続きます。
例21-1 book1.xml
<?xml version="1.0"?> <booklist xmlns="http://booklist.oracle.com"> <book> <title>Twelve Red Herrings</title> <author>Jeffrey Archer</author> <publisher>Harper Collins</publisher> <price>7.99</price> </book> <book> <title language="English">The Eleventh Commandment</title> <author>Jeffrey Archer</author> <publisher>McGraw Hill</publisher> <price>3.99</price> </book> <book> <title language="English" country="USA">C++ Primer</title> <author>Lippmann</author> <publisher>Harper Collins</publisher> <price>4.99</price> </book> <book> <title>Emperor's New Mind</title> <author>Roger Penrose</author> <publisher>Oxford Publishing Company</publisher> <price>15.9</price> </book> <book> <title>Evening News</title> <author>Arthur Hailey</author> <publisher>MacMillan Publishers</publisher> <price>9.99</price> </book> </booklist>
次にあげる結果を除いて、例21-1「book1.xml」に非常によく似たbook2.xml
という別ファイルがあるとします。
「The Eleventh Commandment」を削除(delete-node
操作の1つ)。
「C++ Primer」の国コードを米国から英国に変更(update-node
操作の1つ)。
「Emperor's New Mind」に説明を追加(append-node
操作の1つ)。
「Evening News」に版を追加(insert-node-before
操作の1つ)。
「Evening News」の価格を更新(update-node
操作の1つ)。
この項では、前の項で説明された2つのXMLファイルの比較により生成されたXdiff
インスタンス・ドキュメントを示します。次の項では、XML処理命令およびこの文書での操作について説明します。
次のようなXmlDiff
を起動できます。
> xmldiff book1.xml book2.xml
この表では、引数およびフラグに関するサンプル・アプリケーションについても見ることができます。
例21-2 Xdiffインスタンス・ドキュメントのサンプル
<?xml version="1.0" encoding="UTF-8"?> <xd:xdiff xsi:schemaLocation="http://xmlns.oracle.com/xdb/xdiff.xsd xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oraxdfns_0="http://booklist.oracle.com"> <?oracle-xmldiff operations-in-docorder="true" output-model="snapshot" diff-algorithm="global"?> <xd:delete-node xd:node-type="element" xd:xpath="/oraxdfns_0 :booklist[1]/oraxdfns_0:book[2]"/> <xd:update-node xd:node-type="attribute" xd:parent-xpath="/oraxdfns_0:booklist[1]/oraxdfns_0:book[3]/oraxdfns_0 :title[1]" xd:attr-local="country"> <xd:content>US</xd:content> </xd:update-node> <xd:append-node xd:node-type="element" xd:parent-xpath="/oraxdfns_0 :booklist[1]/oraxdfns_0:book[4]"> <xd:content> <oraxdfns_0:description> This is a classic </oraxdfns_0:description> </xd:content> </xd:append-node> <xd:insert-node-before xd:node-type="element" xd:xpath="/oraxdfns_0 :booklist[1]/oraxdfns_0:book[5]/oraxdfns_0:author[1]"> <xd:content> <oraxdfns_0:edition>Hardcover</oraxdfns_0:edition> </xd:content> </xd:insert-node-before> <xd:update-node xd:node-type="text" xd:xpath="/oraxdfns_0 :booklist[1]/oraxdfns_0:book[5]/oraxdfns_0:price[1]/text()[1]"> <xd:content>12.99</xd:content> </xd:update-node> </xd:xdiff>
Xdiff
インスタンス・ドキュメントは、XML処理命令(前の項の太字表示箇所)を使用して、差分検出処理の特定の側面を表示します。「Xdiffスキーマ」を参照してください。命令および関連オプションは次のとおりです。
operations-in-docorder
: オプションはtrue
またはfalse
です。
true
: Xdiff
インスタンス・ドキュメントは、文書内と同じ順で最初の文書からノードを参照します。
false
: Xdiff
インスタンス・ドキュメントは、文書内と同じ順で最初の文書からノードを参照しません。
グローバル最適化の出力はoperations-in-docorder
要件に適合しますが、ローカル最適化では適合しません。
output-model
: オプションは次のとおりです。
snapshot
: Xmldiffはスナップショット・モデルで出力を生成し、UNIXのdiffモデルに従います。入力文書にどの操作も適用しない場合は、XPath
が使用されます。これはデフォルトです。operations-in-docorder
がtrue
に設定され、XPath
が単純である場合は、XmlPatch
はこのモデルの処理のみ実行できます。単純なXPath
は子の軸をワイルド・カードなしで要求し、述語の位置を/root[1]/child[2]/text()[2]
のように使用する必要があります。
current
: 各操作は、前の操作までのすべての操作が入力文書に適用されているようにXPath
を使用します。XmlDiff
が差分を現在のモデルで生成しない場合でも、XmlPatch
は手作業で作成したdiff
文書を現在のモデルで処理できます。
diff-algorithm
: このオプションはどの最適化が差分を生成するかを示します。
グローバル最適化
ローカル最適化
XmlDiff
は、Xdiff
インスタンス・ドキュメントによって明示される操作を使用して差分を検出します。
Xdiff
の操作は次のとおりです。
操作対象となるノードの親ノードのXPATH
の場所またはノードのXPATH
の場所が、parent-xpath
属性またはxpath
属性によって指定されます。
node-type
属性により、操作対象となるノードのタイプが指定されます。
content
子要素により、追加または挿入される新規のサブツリーまたは値が指定されます。
Xdiff
インスタンス・ドキュメントに表示されるXdiff操作は、次のとおりです。
append-node
append-node
要素により、指定されたタイプのノードが指定された親の最後の子へ追加されるように指定されます。
insert-node-before
insert-node-before
要素により、指定されたタイプのノードが指定された参照ノードの前に挿入されるように指定されます。
delete-node
delete-node
要素により、削除されるノードにそのすべての子が含まれるように指定されます。これは要素やコメントなどを削除する場合に使用されます。
update-node
update-node
は、指定されたXPath
式を含むノードに関連付けられた値が指定された新規の値に更新されるように指定します。内容は、テキスト・ノードに関する値です。属性の値は、属性ノードに関する値です。
テキスト・ノードの更新
update node操作の生成はユーザーによってオフにできます。
属性の値は、属性ノードに関する値です。
update-node
は、グローバル最適化によってのみテキスト・ノードに対して生成されます。
要素の更新
XmlDiff
は、要素ノードに対して更新操作を生成しません。
Xdiff
インスタンス・ドキュメントを手動で変更して、XmlPatch
を操作する更新操作を作成できます。または、Xdiff
インスタンス・ドキュメントを完全に手書きで入力することも可能です。更新操作に関わる要素のすべての子は削除されます。内容ノードで指定された新規のサブツリーは、すべてインポートされます。
XmlDiff
の出力であるXdiff
インスタンス・ドキュメントはXML形式で、次の項に示されるXdiff
スキーマに準拠しています。
出力文書には、2つの入力文書間の差分を示す一連の操作が含まれます。最初の文書から差分を適用する場合は、2番目の文書を取得します。
例21-3に、Xdiff
インスタンス・ドキュメント(出力)が従うXdiff
スキーマを示します。
例21-3 Xdiffスキーマ: xdiff.xsd
<schema targetNamespace="http://xmlns.oracle.com/xdb/xdiff.xsd" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xd="http://xmlns.oracle.com/xdb/xdiff.xsd" version="1.0" elementFormDefault="qualified" attributeFormDefault="qualified"> <annotation> <documentation xml:lang="ja"> Defines the structure of XML documents that capture the difference between two XML documents. Changes that are not supported by Oracle XmlDiff may not be expressible in this schema. 'oracle-xmldiff' PI in Xdiff document: We use 'oracle-xmldiff' PI to describe certain aspects of the diff. The PI denotes values for 'operations-in-docorder' and 'output-model'. The output of XmlDiff has the PI always. If the user hand-codes a diff doc then it must also have the PI in it as the first child of top level xdiff element, to be able to call XmlPatch. operations-in-docorder: Can be either 'true' or 'false'. If true, the operations in the diff document refer to the elements of the input doc in the same order as document order. Output of global algorithm meets this requirement while local does not. output-model: output models for representing the diff. Can be either 'Snapshot' or 'Current'. Snapshot model: Each operation uses Xpaths as if no operations have been applied to the input document. (like UNIX diff) This is the model used in the output of XmlDiff. XmlPatch works with this (and the current model too). For XmlPatch to handle this model, "operations-in-docorder" must be true and the Xpaths must be simple. (see XmlDif C API documentation). Current model: Each operation uses Xpaths as if all operations till the previous one have been applied to the input document. Works with XmlPatch even if the 'operations-in-docorder' criterion is not met and the xpaths are not simple. <!-- Example: <?oracle-xmldiff operations-in-docorder="true" output-model= "snapshot" diff-algorithm="global"?> --> </documentation> </annotation> <!-- Enumerate the supported node types --> <simpleType name="xdiff-nodetype"> <restriction base="string"> <enumeration value="element"/> <enumeration value="attribute"/> <enumeration value="text"/> <enumeration value="cdata"/> <enumeration value="entity-reference"/> <enumeration value="entity"/> <enumeration value="processing-instruction"/> <enumeration value="notation"/> <enumeration value="comment"/> </restriction> </simpleType> <element name="xdiff"> <complexType> <choice minOccurs="0" maxOccurs="unbounded"> <element name="append-node"> <complexType> <sequence> <element name="content" type="anyType"/> </sequence> <attribute name="node-type" type="xd:xdiff-nodetype"/> <attribute name="xpath" type="string"/> <attribute name="parent-xpath" type="string"/> <attribute name="attr-local" type="string"/> <attribute name="attr-nsuri" type="string"/> </complexType> </element> <element name="insert-node-before"> <complexType> <sequence> <element name="content" type="anyType"/> </sequence> <attribute name="xpath" type="string"/> <attribute name="node-type" type="xd:xdiff-nodetype"/> </complexType> </element> <element name="delete-node"> <complexType> <attribute name="node-type" type="xd:xdiff-nodetype"/> <attribute name="xpath" type="string"/> <attribute name="parent-xpath" type="string"/> <attribute name="attr-local" type="string"/> <attribute name="attr-nsuri" type="string"/> </complexType> </element> <element name="update-node"> <complexType> <sequence> <element name="content" type="anyType"/> </sequence> <attribute name="node-type" type="xd:xdiff-nodetype"/> <attribute name="parent-xpath" type="string"/> <attribute name="xpath" type="string"/> <attribute name="attr-local" type="string"/> <attribute name="attr-nsuri" type="string"/> </complexType> </element> <element name="rename-node"> <complexType> <sequence> <element name="content" type="anyType"/> </sequence> <attribute name="xpath" type="string"/> <attribute name="node-type" type="xd:xdiff-nodetype"/> </complexType> </element> </choice> <attribute name="xdiff-version" type="string"/> </complexType> </element> </schema>
アプリケーションでは、XmlDiff
は、入力文書のソース・タイプおよび場所を引数とします。ソース・タイプは、URL、ファイル、orastream
およびstream
コンテキスト・ポインタ、バッファ、buffer_length
ポインタ、またはDOMドキュメント要素(docelement
)へのポインタです。
XmlDiff
は、Xdiff
インスタンス・ドキュメントに関するDOMの文書ノードを戻します。
比較の実行前にDOMとして2つの文書が提供されていない場合、XmlDiffは2つの文書に対してDOMを構築します。
関連項目: XmlDiff の動作を制御するフラグのC APIの詳細は、『Oracle Database XML C APIリファレンス』を参照してください。 |
例21-4 XMLDiffアプリケーション
# include <xmldf.h> ... xmlctx *xctx; xmldocnode *doc1, *doc2, *doc3; uword hash_level; oratex *s, *inp1 = "book1.xml", *inp2="book2.xml"; xmlerr err; ub4 flags; flags = 0; /* defaults : global algorithm */ hash_level = 0; /* no hashing */ /* create XML meta context */ if (!(xctx = XmlCreate(&err, (oratext *) "XmlDiff", NULL))) { printf("Failed to create XML context, error %u\n", (unsigned) err); err_exit("Exiting"); } /* Load the two input files */ if (!(doc1 = XmlLoadDom(xctx, &err, "file", inp1, "discard_whitespace", TRUE, NULL))) { printf("Parsing first file failed, error %u\n", (unsigned)err); err_exit((oratext *)"Exiting."); } if (!(doc2 = XmlLoadDom(xctx, &err, "file", inp2, "discard_whitespace", TRUE, NULL))) { printf("Parsing second file failed, error %u\n", (unsigned)err); err_exit((oratext *)"Exiting."); } /* run XmlDiff on the DOM trees. */ doc3 = XmlDiff(xctx, &err, flags, XMLDF_SRCT_DOM, doc1, NULL, XMLDF_SRCT_DOM, doc2, NULL,hash_level, NULL); if(!doc3) printf("XmlDiff Failed, error %u\n", (unsigned)err); else { if(err != XMLERR_OK) printf("XmlDiff returned error %u\n", (unsigned)err); /* Now we have the DOM tree in doc3 which represent the Diff */ ... } XmlFreeDocument(xctx, doc1); XmlFreeDocument(xctx, doc2); XmlFreeDocument(xctx, doc3); XmlDestroy(xctx);
カスタマイズ済出力ビルダーは、アプリケーションに適したあらゆる形式の差分を格納します。XmlDiff
によって生成され、Xdiff
スキーマに準拠したデフォルトのXdiff
インスタンス・ドキュメントを使用するかわりに、ユーザー自身のカスタマイズ済出力ビルダーを作成できます。
これを実行するには、XmlDiff
が差分を決定した後にコールされるコールバックを提供する必要があります。差分はxmdlfop
の配列としてコールバックに渡されます。差分が生成されるたびに、コールバックが呼び出される場合があります。
XPath
生成に必要な内部状態の保守が不要なため、カスタマイズ済出力ビルダーを使用する方が、デフォルトを使用するパフォーマンスが向上する場合があります。
デフォルトでは、XmlDiff
はXdiff
スキーマに準拠するXMLでの差分を検出します。必要な場合は、出力ビルダーをプラグインしてください。差分は配列xmldfop
として表示されます。出力ビルダーのコールバック関数を書き込む必要があります。関数署名は次のとおりです。
xmlerr(*xdfobcb)(void *uctx, xmldfop *escript, ub4 escript_siz);
uctx
は、ユーザー指定のコンテキストです。
escript
は、サイズescript_siz
の配列です。
diff[escript_siz]
mctx
は、メモリー・コンテキストです。
プロパティを介してこのメモリー・コンテキストをXmlDiff()
に提供します。このメモリー・コンテキストを使用して、escript
に割り当てます。後でescript
を解放する必要があります。
XmlDiff()
のコールが戻る前でも発生する差分が検出されたら、出力ビルダーのコールバックを起動します。出力ビルダーのコールバックは、複数回コールされます。
例21-5 カスタマイズ済XMLDiff出力
/* Sample useage: */ ... #include <orastruc.h> / * for 'oraprop' * / ... static oraprop diff_props[] = { ORAPROP(XMLDF_PROPN_CUSTOM_OB, XMLDF_PROPI_CUSTOM_OB, POINTER), ORAPROP(XMLDF_PROPN_CUSTOM_OBMCX, XMLDF_PROPI_CUSTOM_OBMCX, POINTER), ORAPROP(XMLDF_PROPN_CUSTOM_OBUCX, XMLDF_PROPI_CUSTOM_OBUCX, POINTER), { NULL } }; ... oramemctx *mymemctx; ... xmlerr myob(void *uctx, xmldfop *escript, ub4 escript_siz) { /* process diff which is available in escript * / /* free escript - the caller has to do this * / OraMemFree(mymemctx, escript); } main() { ... myctxt *myctx; diff_props[0].value_oraprop.p_oraprop_v = myob; diff_props[1].value_oraprop.p_oraprop_v = mymemctx; diff_props[2].value_oraprop.p_oraprop_v = myctx; XmlDiff(xctx, &err, 0, doc1, NULL, 0, doc2, NULL, 0, diff_props); ... }
XmlPatch
は、XmlDiff
によって生成されたものまたは別のメカニズムにより作成されたもののいずれかのXdiff
インスタンス・ドキュメントを受け取り、Xdiff
インスタンス・ドキュメント内の指示に従って、指定された他のXML文書を修正します。
表21-2に、XmlPatch
コマンドライン・オプションを示します。
XmlPatch
は、入力文書およびdiff
文書のソース・タイプおよび場所を引数とします。ソース・タイプは、URL、ファイル、orastream
およびstream
コンテキスト・ポインタ、バッファ、buffer_length
ポインタ、またはDOMドキュメント要素(docelement
)へのポインタです。
関連項目: XmlPatch の動作を制御するフラグのC APIの詳細は、『Oracle Database XML C APIリファレンス』を参照してください。 |
Xdiff
スキーマによって設定されたモードはXmlPatch
の作業に影響します。
output-model
がSnapshot
の場合、XmlPatch
は、operations-in-docorder
がTRUE
の場合のみ作業します。
output-model
がCurrent
の場合、operations-in-docorder
をTRUE
に設定する必要はありません。
例21-6 XmlPatchに関するサンプル・アプリケーション
... #include <xmldf.h> ... xmlctx *xctx; xmldocnode *doc1, *doc2; oratext *s; oratext *inp1 = "book1.xml"; /* input document */ oratext *inp2 = "diff.xml", /* diff document */ xmlerr err; /* create XML meta context */ if (!(xctx = XmlCreate(&err, (oratext *) "XmlPatch", NULL))) { printf("Failed to create XML context, error %u\n", (unsigned) err); err_exit("Exiting"); } /* Load the two input files */ if (!(doc1 = XmlLoadDom(xctx, &err, "file", inp1, "discard_whitespace", TRUE, NULL))) { printf("Parsing first file failed, error %u\n", (unsigned)err); err_exit((oratext *)"Exiting."); } if (!(doc2 = XmlLoadDom(xctx, &err, "file", inp2, "discard_whitespace", TRUE, NULL))) { printf("Parsing second file failed, error %u\n", (unsigned)err); err_exit((oratext *)"Exiting."); } /* call XmlPatch */ if(!XmlPatch(xctx, &err, 0, XMLDF_SRCT_DOM, doc1, NULL, XMLDF_SRCT_DOM, doc2, NULL, NULL)); printf("XmlPatch Failed, error %u\n", (unsigned)err); else { if(err != XMLERR_OK) printf("XmlPatch returned error %u\n", (unsigned)err); /* Now we have the patched document in doc1 */ ... } XmlFreeDocument(xctx, doc1); XmlFreeDocument(xctx, doc2); XmlDestroy(xctx);
Oracle XDKにより、XMLツリーまたはサブツリーに対するハッシュ値を計算するXmlHash,
が提供されます。2つのサブツリーのハッシュ値が同一の場合、同じXMLである可能性が非常に高いです。たとえば、XMLツリーがすでにデータベース内にあるかどうかを見る場合、これを使用してすばやく比較できます。
必要な場合、一致がある可能性がある一致に対してXmlDiff
を再度実行できます。新規文書のハッシュ値を計算して、データベースに問合せを実行できます。
例21-7に、XmlHash
を使用するサンプル・プログラムを示します。
例21-7 XmlHashプログラム
sword main(sword argc, char *argv[]) { xmlctx *xctx; xmldfsrct srct; oratext *data_encoding, *input_encoding, *s, *inp1; ub1 flags; xmlerr err; ub4 num_args; xmlhasht digest; flags = 0; /* defaults */ srct = XMLDF_SRCT_FILE; inp1 = "somexml.xml"; xctx = XmlCreate(&err, (oratext *) "XmlHash", NULL); if (!xctx) { /* handle error with creating xml context and exit */ ... } /* run XmlHash */ err = XmlHash(xctx, &digest, 0, srct, inp1, NULL, NULL); if(err) printf("XmlHash returned error:%d \n", err); else txdfha_pd(digest); XmlDestroy(xctx); return (sword )err; } /* print bytes in xml hash */ static void txdfha_pd(xmlhasht digest) { ub4 i; for(i = 0; i < digest.l_xmlhasht; i++) printf("%x ", digest.d_xmlhasht[i]); printf("\n"); }