XMLマスターポイントレッスン
~ プロフェッショナル(アプリケーション開発)編 ~
第3回 DOM/SAXプログラミング
インフォテリア株式会社 教育部 木村 達哉
前回は、「XMLマスター:プロフェッショナル(アプリケーション開発)」認定試験のセクション1で出題範囲となるDOM/SAXの基本仕様について説明しました。続く今回は、セクション2で出題される、DOM/SAXプログラミング関連問題のポイントを解説します。
セクション2のポイント
セクション2では、単純なXML文書とともに、DOM APIもしくはSAX APIを使用したJavaプログラムが提示され、その処理結果を問うような問題が出題されます。問題の中で提示されるプログラムは至ってシンプルなものですが、XMLデータを扱う際のDOM/SAX APIの振る舞いを正確に理解している必要があります。
以降では、DOMプログラム、またはSAXプログラムに関する例題/解答を提示するとともに、その問題においてポイントとなる個所を解説していきます。
DOMプログラムに関する問題
では早速、例題をご覧いただきましょう。なお、以下に示すのは、本連載の第1回目において、『試験における出題例』として紹介したものと同じ問題です。
●DOMプログラムを例示する出題例――その(1)
次の《XML文書》を読み込み、《DOMによる処理》で処理を行います。その結果をXML 1.0で表現したものとして、最も適切なものを選択してください。ただし、改行やインデントは考慮しないものとします。
《XML文書》
<parent>
<child1>DATA1</child1> <child2>DATA2</child2> </parent> |
《DOMによる処理》
次のコードでXMLを処理します。
Document output = updateXML(doc); |
このとき、変数docは、読み込んだXML文書のDocumentインスタンスを参照しています。実行時のエラーはないものとします。メソッドupdateXMLのコードは、以下のようになります。
public static Document updateXML(Document doc) {
Node child1 = doc.getElementsByTagName("child1").item(0); Element parent = doc.getDocumentElement(); parent.appendChild( child1 ); return doc; } |
●選択肢
A.
<parent>
<child1>DATA1</child1> <child2>DATA2</child2> </parent> |
B.
<parent>
<child2>DATA2</child2> <child1>DATA1</child1> </parent> |
C.
<parent>
<child1>DATA1</child1> <child1>DATA1</child1> <child2>DATA2</child2> </parent> |
D.
<parent>
<child1>DATA1</child1> <child2>DATA2</child2> <child1>DATA1</child1> </parent> |
●解答
B
●解説
この問題に正答するには、DOM APIに用意されたメソッドappendChildの振る舞いを理解している必要があります。appendChildは、対象となるノードの最後部に、引数で指定された要素ノードを追加するメソッドです。では、提示されたメソッドupdateXMLのコードで行われている処理を順に読み解いていきましょう。
メソッドupdateXMLでは、まずメソッドgetElementsByTagNameによって要素child1を取得しています。メソッドgetElementsByTagNameは、対象となるノードから、引数に指定した要素を検出するメソッドです。
続いて、メソッドgetDocumentElementでルート要素(要素parent)を取得した後、そのルート要素に対してメソッドappendChildを実行し、ノードの最後部(すなわち、要素child2の次)に要素child1を追加するという流れになっています(このとき、要素child1は、元の位置から切り離されます)。
また、問題文にある「実行時のエラーはない」という一文から、Javaプログラムに単純な誤り(要素名や名前空間の記述ミスなど)は含まれていないことがわかります。
問題のように、処理するXML文書内にインデントが含まれている場合、DOMパーサ実行時の設定が「空白のみのテキスト・ノードを保持する」になっていれば、処理結果にも空白のみのテキスト・ノードの位置関係が反映されます。この場合の処理結果をXML 1.0で表現すると、以下のようになります。
<parent>
<child2>DATA2</child2> <child1>DATA1</child1></parent> |
ただし、問題では「改行やインデントは考慮しない」としているので、DOMパーサの設定にかかわらず、正解はBとなります。設問によっては「改行やインデントを考慮する」とされている場合もあるので、問題文をよく確認しましょう。
なお、前回も説明しましたが、XMLマスター試験で出題されるDOM関連の問題は、DOM Level 2に基づくものです。そのため、この例題でも、同仕様では規定されていないXML文書の読み込み方法については言及していません。
さて、上の例題は、単にメソッドappendChildの振る舞いを問うだけのものです。このほかに、名前空間を使ったXML文書に対して何らかの処理を行った場合、名前空間にどのような影響が及ぶのかを問うような問題が出題されることもあります。その例として、上の例題をベースにした応用問題を紹介しておきましょう。
●DOMプログラムを例示する出題例――その(2)
次の《XML文書》を読み込み、《DOMによる処理》で処理を行います。その結果をXML 1.0で表現したものとして、最も適切なものを選択してください。ただし、改行やインデントは考慮しないものとします。
《XML文書》
<parent>
<child1>DATA1</child1> <child2 xmlns="urn:xmlmaster:sample">DATA2</child2> </parent> |
《DOMによる処理》
次のコードでXML文書を処理します。
Document output = updateXML(doc); |
このとき、変数docは、読み込んだXML文書のDocumentインスタンスを参照しています。実行時のエラーはないものとします。メソッドupdateXMLのコードは、以下のようになります。
public static Document updateXML(Document doc) {
Node child1 = doc.getElementsByTagNameNS(null, "child1").item(0); Node child2 = doc.getElementsByTagNameNS("urn:xmlmaster:sample", "child2").item(0); child2.appendChild(child1); return doc; } |
●選択肢
A.
<parent>
<child2 xmlns="urn:xmlmaster:sample">DATA2<child1>DATA1</child1></child2> </parent> |
B.
<parent>
<child2 xmlns="urn:xmlmaster:sample">DATA2<child1 xmlns="">DATA1</child1></child2> </parent> |
C.
<parent>
<child2 xmlns="urn:xmlmaster:sample"><child1>DATA1</child1>DATA2</child2> </parent> |
D.
<parent>
<child2 xmlns="urn:xmlmaster:sample"><child1 xmlns="">DATA1</child1>DATA2</child2> </parent> |
●解答
B
●解説
この問題を解くには、メソッドappendChildの振る舞いに関する知識だけでなく、XML文書を処理した後の名前空間の扱いについての知識も必要となります。DOM APIを用いたXML文書の操作でノードの移動を行った場合、ノードの名前空間は変わりません。このメソッドの実行結果に合致しているのは、選択肢Aと選択肢Bです。ただし、選択肢Aは前述の名前空間の規則に合致せず、DOMパーサーによるシリアライズ後のXML文書であるため、正解はBとなります。
DOM Level 2 Coreでは、DOM APIによるXML文書の操作と名前空間に関して、次のように定めています。
DOM操作でノードの移動を行った場合、ノードの名前空間は変わらない。また、ノードの移動に伴って必要となる名前空間の宣言は、追加しなくてもよい。 |
したがって、DOMパーサ(あるいは実行環境)によっては、DOM APIによる操作結果(DOMツリー)のシリアライズを自動的に行い、選択肢AのようなXML文書を出力する場合もあるかもしれません。
しかし、問題文では、「その結果をXML 1.0で表現したものとして、最も適切なものを選択してください」としています。これは、シリアライズ後のXML文書ではなく、「DOMツリーとしてどのような結果が得られるのか」を尋ねているのです。
SAXプログラムに関する問題
続いて、SAXプログラムに関する例題を2つ示します。SAXプログラムを例示する問題の場合も、DOMの場合と同様、XML文書とそれを処理するSAXプログラムが示され、選択肢から処理結果を選択する形式となります。
●SAXプログラムを例示する出題例――その(1)
次の《XML文書》を、《SAXによる処理》で示された方法で処理した場合、その出力結果(メソッドprintによる出力)として最も適切なものを選択してください。なお、XML文書内のインデント(改行やタブなどの無意味な空白文字)を考慮するものとします。
《XML文書》
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE root [ <!ELEMENT root (body*)> <!ELEMENT body (#PCDATA)> <!ATTLIST body section CDATA "0"> ]> <root> <body/> <body section="1">XMLプログラミング</body> </root> |
《SAXによる処理》
次に示すクラスContentHandlerImplを使用し、SAX APIによってXML文書を処理します。SAXパーサは、妥当性の検証を行うように設定されています。また、実行時のエラーはないものとします。
class ContentHandlerImpl extends DefaultHandler {
public void characters(char[] ch, int start, int length) throws SAXException { System.out.print("文字"); } public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { System.out.print("[空白]"); } } |
●選択肢
A.
[空白][空白]文字[空白]
B.
文字文字文字文字
C.
文字文字文字
D.
文字
●解答
A
●解説
この例題は、メソッドignorableWhitespaceに関する理解を問うものです。
まずは、問題で提示されている前提条件を整理しましょう。この例題では、「XML文書内のインデント(改行やタブなどの無意味な空白文字)を考慮する」としています。また、「実行時のエラーはない」という一文から、XML文書の整形式の制約違反や妥当性の制約違反は含まれていないことがわかります。問題を読む際には、こうした前提条件を見落とさないようにしてください。では、例題を順に読み解いていきましょう。
SAXパーサは、要素の内容である文字データに対して、メソッドcharactersを呼び出します。このとき、SAXパーサは妥当性の検証を行うように設定されているので、ある要素の内容が子要素のみである(つまり、その要素が内容文字列を含まない)場合には、子要素とともに並ぶ空白(改行文字や空白文字)に対して、メソッドignorableWhitespaceを呼び出します。
この規則を踏まえて提示されたXML文書を追ってみると、まず空要素bodyの前後の空白※1に対応して、「[空白]」が2回出力されます。続いて、要素bodyの内容である「XMLプログラミング」(文字データ)に対応して、「文字」が出力されます。そして最後に、2番目の要素bodyの後ろの空白(改行文字)に対応して、「[空白]」が1回出力されるわけです。したがって、正解はAとなります。
※1空要素bodyの前部分には、正確に言えば、改行文字と空白文字(2文字分のインデント)が存在する。ただし、この場合、改行文字と空白文字を合わせて1つと数えるか、2つ、ないし3つと数えるかは、SAXパーサの実装に依存する。ここでは、1つと見なしている。
●SAXプログラムを例示する出題例――その(2)
次の《XML文書》を、《SAXによる処理》で示された方法で処理した場合、その出力結果(メソッドprintによる出力)として最も適切なものを選択してください。なお、XML文書内のインデント(改行やタブなどの無意味な空白文字)は考慮しないものとします。
《XML文書》
<?xml version="1.0" encoding="Shift_JIS"?>
<!DOCTYPE root [ <!ELEMENT root (body*)> <!ELEMENT body (#PCDATA)> <!ATTLIST body section CDATA "0"> ]> <root> <body/> <body section="1"><!--Comment--><![CDATA[DOM&SAX]]>プログラミング</body> </root> |
《SAXによる処理》
次に示すクラスContentHandlerImplを使用し、SAX APIによってXML文書を処理します。SAXパーサは、妥当性の検証を行いません。また、実行時のエラーはないものとします。
class ContentHandlerImpl extends DefaultHandler {
public void characters(char[] ch, int start, int length) throws SAXException { System.out.print(new String(ch, start, length)); } } |
●選択肢
A.
01CommentDOM&SAXプログラミング
B.
1CommentDOM&SAXプログラミング
C.
CommentDOM&SAXプログラミング
D.
DOM&SAXプログラミング
E.
プログラミング
●解答
D
●解説
この例題を解くには、SAX APIのメソッドcharactersが、XML文書内のどういった種類の文字列を返すのかについての知識が必要となります。提示されたXML文書の中で、メソッドcharactersが返さないのは、以下に該当する個所です。
●ルート要素に含まれない宣言部分(XML宣言や文書型宣言など)
●属性値
●コメント
一方、メソッドcharactersが返すのは、CDATAセクション(<![CDATA[という文字列で始まり、]]>という文字列で終わる部分)内に定義された文字列を含む要素bodyの内容です。したがって、正解はDとなります。
以上、今回は、セクション2の出題対象となる、DOM/SAXプログラミング関連の問題のポイントについて解説しました。名前空間や空白文字、また今回は触れませんでしたが、実体参照が含まれるXML文書の扱い方などについての知識は、試験で問われるだけでなく、実務においても必須となります。しっかりと学習しておきましょう。