7. DOMと名前空間
7.1. 名前空間とDOM
DOMでは、Level2より名前空間に対応しています。以下のような操作を行うことが可能になりました。
- 特定の名前空間に属する要素の内容を取得する
- 特定の名前空間に属する属性ノードを作成する 。。。など
ただし、名前空間に対応したメソッドには、少し「くせ」があります。以下、DOMで名前空間を指定したXML文書を扱う方法と注意点について、説明します。
7.2. XMLを読む
名前空間対応に伴い、DOM Level 2では、名前空間URIとローカル名を指定してノードを取得するメソッドが追加されました。
例えば、Elementには要素のリストを取得するgetElementsByTagName(String name)というメソッドが用意されています。これは、引数に指定されたタグ名の要素を取得するメソッドです。対して、名前空間を指定して要素を取得する getElementsByTagNameNS(String namespaceURI,String localName)が追加されました。引数には、ネームスペースURIとローカル名を指定します。その他にも、Elementノードには、以下のメソッドが提供されています。
メソッド | 内容 |
---|---|
public NodeList getElementsByTagNameNS(String
namespaceURI, String localName) |
所定のローカル名およびネームスペース URI とともに、すべての Elements の NodeList を返します。返される順番は、この Element ツリーの先行順 (preorder traversal) で検出された順番になります。 |
public String getAttributeNS(String namespaceURI, String localName) |
ローカル名とネームスペース URI を指定して属性値を取得します。 |
public Attr getAttributeNodeNS(String
namespaceURI, String localName) |
ローカル名とネームスペース URI を指定して Attr ノードを取得します。 |
public boolean hasAttributeNS(String namespaceURI, String localName) |
この要素上に所定のローカル名とネームスペース URI を持つ属性が指定された場合、またはその属性にデフォルト値がある場合は true、それ以外の場合は false |
例を挙げて、説明しましょう。以下のようなXML文書があったとします。
<?xml version="1.0" encoding="UTF-8"?> <html xmlns="http://www.w3.org/1999/xhtml"> <body> <form action="/sample2/productInput.jsp" method="post"> <table> <tr><td>商品名</td><td><input type="text" name="name" /></td></tr> <tr><td>メーカ名</td><td><input type="text" name="maker"/></td><f/tr> </table> <input type="submit" value="登録" /> </form> <html:form action="/sample/productInput.do" method="post" xmlns:html="http://jakarta.apache.org/struts/tags-html-1.0"> <table> <tr><td>商品名</td><td><html:text property="name" /></td></tr> <tr><td>メーカ名</td><td><html:text property="maker"/></td></tr> </table> <html:submit value="登録" /> </html:form> </body> </html>
上のXML文書には、名前空間URIが2つ登場します。まず、html要素で宣言している"http://www.w3.org/1999/xhtml"です。これは、ルート要素の、xmlns属性の値として宣言しているので、名前空間接頭辞を持たないすべての要素(body,table,tr...)はこの名前空間に属しています。4行目のformも、"http://www.w3.org/1999/xhtml"で表される名前空間に属します。
2つ目は、12行目で宣言している"http://jakarta.apache.org/struts/tags-html-1.0"です。この名前空間URIは"xmlns:html"属性の値として宣言しています。これは、html接頭辞がついている要素・属性は、この名前空間に属すということを表しています。この文書では、11行目のform要素、および15,16行目のtext要素が"http://jakarta.apache.org/struts/tags-html-1.0"名前空間に属します。
このXML文書を読み込み、値を取得するプログラムを以下のように作成しました。
1 import java.io.*; 2 import org.w3c.dom.Document; 3 import org.w3c.dom.DocumentType; 4 import org.w3c.dom.Element; 5 import org.w3c.dom.Node; 6 import org.w3c.dom.NodeList; 7 8 import javax.xml.parsers.*; 9 10 public class NamespaceSample{ 11 12 public static void main(String args[]) throws Exception{ 13 14 System.out.println("filename is "+args[0]); 15 DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance(); 16 factory.setNamespaceAware(true); 17 DocumentBuilder builder=factory.newDocumentBuilder(); 18 Document document= builder .parse(new File(args[0])); 19 20 Element root=document.getDocumentElement(); 21 22 NodeList formWithPrefix 23 =root.getElementsByTagNameNS("http://jakarta.apache.org/struts/tags-html-1.0","form"); 24 System.out.println("form elemenet (NS = http://jakarta.apache.org/struts/tags-html-1.0): num="+formWithPrefix.getLength()); 25 for(int i=0;i<formWithPrefix.getLength();i++){ 26 Element element=(Element)formWithPrefix.item(i); 27 System.out.println("action attribute value is "+element.getAttributeNS(null,"action")); 28 } 29 30 NodeList formInDefaultSpace 31 =root.getElementsByTagNameNS("http://www.w3.org/1999/xhtml","form"); 32 System.out.println("form elemenet (NS=http://www.w3.org/1999/xhtml): num="+formInDefaultSpace.getLength()); 33 34 for(int i=0;i<formInDefaultSpace.getLength();i++){ 35 Element element=(Element)formInDefaultSpace.item(i); 36 System.out.println("action attribute value is "+element.getAttributeNS(null,"action")); 37 } 38 39 } 40 }
名前空間を扱うには、パーサの設定を変更する必要があります。デフォルトでは、Java 1.4 付随のXMLパーサは名前空間を認識しません。16行目で、DocumentBuilderFactoryのsetNamespaceAware()メソッドを使用して、名前空間を認識するよう設定します。
22-23行目では、URI "http://jakarta.apache.org/struts/tags-html-1.0"で表される名前空間に属するform要素のNodeListを取得しています。24行目では、取得したNodeListの長さを表示しています。"http://jakarta.apache.org/struts/tags-html-1.0"に属しているform要素は、ひとつだけです。27行目では、action属性の値を取得し、表示しています。属性ノードの取得のために、getAttributeNSメソッドを使用しています。このメソッドは、getAttributeメソッドを名前空間に対応させたもので、引数に名前空間URIと属性のローカル名を指定します。
属性の名前空間について、復習しましょう。
- 属性に名前空間接頭辞がついている場合、その属性は接頭辞が表す名前空間に属する
- 属性に名前空間接頭辞がついていない場合、その属性は、要素毎のローカルな名前空間に属する。URIで表される名前空間に属さない。
このaction属性は名前空間接頭辞を持ちません。よって、要素毎の区画に属することになります。このような場合、名前空間URIには null を指定します。27行目でgetAttributeNSメソッドでも、名前空間URIとしてnull を指定しています。つまり、名前空間に対応した属性操作メソッドで名前空間URIにnullを指定した場合は、名前空間接頭辞を持たない属性を指定していることになります。27行目では、action属性の値を出力していますが、その値は、"/sample2/productInput.jsp"になるはずです。
同様に、30-31行目では"http://www.w3.org/1999/xhtml"名前空間に属するform要素を取得しています。これも、デフォルト名前空間"http://www.w3.org/1999/xhtml"に属するform要素はひとつだけです。また、36行目で、getAttributeNSメソッドを用いてaction属性の値を取得しています。このaction属性も名前空間接頭辞を持ちません。名前空間URIにnullを指定しています。actionの値が"/sample2/productInput.jsp"であることを確認できます。上記プログラムを実行すると、以下のように出力されます。
$ java NameSpaceSample sample.xml filename is sample.xml form elemenet (NS = http://jakarta.apache.org/struts/tags-html-1.0): num=1 action attribute value is /sample/productInput.do form elemenet (NS=http://www.w3.org/1999/xhtml): num=1 action attribute value is /sample2/productInput.jsp
getElementsByTagNSメソッドでは、「全ての名前空間」「全てのローカル名」という意味で、"*"を引数に設定することができます。例えば、以下のプログラムです。
1 import java.io.*; 2 import org.w3c.dom.Document; 3 import org.w3c.dom.DocumentType; 4 import org.w3c.dom.Element; 5 import org.w3c.dom.Node; 6 import org.w3c.dom.NodeList; 7 8 import javax.xml.parsers.*; 9 10 public class NamespaceSample2{ 11 12 public static void main(String args[]) throws Exception{ 13 … 20 Element root=document.getDocumentElement(); 21 22 NodeList form 23 =root.getElementsByTagNameNS("*","form"); 24 System.out.println("form elemenet : num="+form.getLength()); 25 for(int i=0;i<form.getLength();i++){ 26 Element element=(Element)form.item(i); 27 System.out.println("action attribute value is "+element.getAttributeNS(null,"action")); 28 } 29 30 } 31 }
上のプログラムを実行すると、以下のようになります。
$ java NamespaceSample2 sample.xml filename is sample.xml form elemenet : num=2 action attribute value is /sample2/productInput.jsp action attribute value is /sample/productInput.do
それでは、名前文字列URIとしてnullを指定したら、どうなるでしょう。getElementsByTagNameNSメソッドで名前空間URIとしてnullを指定すると、"*"を指定したのと同じ結果が得られます。つまり、ローカル名が引数で与えられたタグ名と一致する全ての要素ノードを返します。
(実習課題1)
与えられたXMLを解析するプログラムを作成しなさい。
- 名前空間URI "http://schemas.xmlsoap.org/soap/envelope/"、ローカル名"Body"で表される要素の内容を取り出すコンソールプログラム。
- 名前空間URI "http://schemas.xmlsoap.org/soap/envelope/"、ローカル名"Body"で表される要素は、文書中に必ず出現するものとする。Body要素はルート要素ではなく、2回以上出現しないものとする。
- 例えば、以下のXML文書があったとします。
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> <soapenv:Body> <m:customer xmlns:m="http://www.techscore.com/customerMessage/"> <m:name>山田太郎</m:name> <m:age>31</m:age> </m:customer> </soapenv:Body> </soapenv:Envelope>
出力結果は以下のようになります。1番目の要素: 名前空間URI:http://www.techscore.com/customerMessage/ ローカル名:customer 2番目の要素: 名前空間URI:http://www.techscore.com/customerMessage/ ローカル名:name 内容:山田太郎 3番目の要素; 名前空間URI:http://www.techscore.com/customerMessage/ ローカル名:age 内容:32
- (ヒント)名前空間URIを取得するには、NodeのgetNamespaceURIメソッドを使用します。
- (ヒント)ローカル名を取得するには、NodeのgetLocalNameメソッドを使用します。
- bodyの内容には、要素とテキストのみが存在し、名前空間宣言以外の属性やコメント、エンティティは存在しないものとする。
- body、およびその子供要素は、要素とテキストが混在するような内容を持たないものとする。つまり、body、およびその子供要素の内容は、複数の要素のみ、あるいは、テキストのみからなる。