java xpath 命名空间_【转】玩转 XPath 和缺省命名空间(Default Namespaces)
諸如“為什么用 XPath 的表達式進行查詢,卻沒有返回所期望的結果?”的問題通常都與命名空間(NameSpace)有關,而且絕大多數是與缺省命名空間(Default Namespace)有關。本文試圖解釋這個問題并針對三種流行的 XPath 實現給出解決方法:Jaxen、JAXP XPPathFactory 以及 XSLT。
內容列表????1. 問題描述
2. “前綴-命名空間”映射
3. Jaxen 和 Dom4J
4. Jaxen 和 XOM
5. Jaxen 和 JDOM
6. JAXP XPathFactory
7. XSLT
8.?結束語
9. 資源
問題描述
看下述 XML:
Sufjan Stevens
Illinoishttp://www.sufjan.com/
Stoat
Future come and get mehttp://www.stoatmusic.com/
The White Stripes
Get behind me satanhttp://www.whitestripes.com/
你可以使用“//cd”來得到沒有在任何命名空間中定義的“cd”節點。
現在讓我們來改造這個 XML,讓它的所有元素都屬于 'http://www.edankert.com/examples/' 命名空間中。
為了避免在每個不同的元素前都要加個前綴,我們在根元素上定義通常所說的缺省命名空間。改造后的 XML 如下:
Sufjan Stevens
Illinoishttp://www.sufjan.com/
Stoat
Future come and get mehttp://www.stoatmusic.com/
The White Stripes
Get behind me satanhttp://www.whitestripes.com/
當我們使用與上文相同的 XPath “//cd”,將得不到任何元素。這是因為指定的 XPath 返回的是所有不屬于任何命名空間的“cd”節點,而本例中,所有的“cd”元素都屬于缺省的命名空間“http://www.edankert.com/examples/”。
“前綴-命名空間”映射
為了取出命名空間“http://www.edankert.com/examples/”中的所有“cd”元素,我們需要對 XPath 表達式做一些額外的工作。
為了解決這個問題,XPath 規范允許我們使用 QName 來指定元素或者屬性。QName 可以是元素的直接名稱(形如“element”),或者包含一個前綴(形如“pre:element”)。這個前綴需要映射到一個命名空間的 URI 上。例如,如果把“pre”前綴映射到“http://www.edankert.com/test”上,則通過“pre:element”可以查找出屬于命名空間“http://www.edankert.com/test”的所有 “element”元素。
在本例中,我們把“edx”映射到“'http://www.edankert.com/examples/”命名空間上。通過 XPath“//edx:cd”就可以查找出屬于“'http://www.edankert.com/examples/”命名空間的所有“cd”元素。
XPath 處理器允許設置“前綴-命名空間”的映射,但是,如何去映射,卻要依賴于具體的實現。下文舉例說明 Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何進行“前綴-命名空間”的映射的。
Jaxen 和 Dom4J
下述代碼從文件系統讀入一個 XML 文件到 org.dom4j.Document 對象中,并且在 Document 中查找屬于“http://www.edankert.com/examples/”命名空間的所有“cd”元素。
try {
SAXReader reader = new SAXReader();
Document document = reader.read( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new Dom4jXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
// the document is not well-formed.
...
}
第一步,創建一個 SAXReader,用來從文件系統中讀取“catalog.xml”并創建一個特定于 Dom4j 的 Document 對象。
第二步,對于所有 Jaxen 實現都一樣,就是創建一個 HashMap 對象,用于保存“前綴-命名空間的 URI”的映射。
為了能通過 Dom4j 使用 Jaxen 的 XPath 功能,需要創建一個與 Dom4j 相關的 XPath 對象:Dom4jXPath。創建方法是把 XPath 的表達式(即“//edx:cd”)傳給 Dom4jXPath 的構造方法。
現在,我們已經創建了 XPath 對象,接下來可以把“前綴-命名空間”的映射表傳遞給 XPath 引擎:把這個 HashMap 映射表用 SimpleNamespaceContext 包裝起來。SimpleNamespaceContext 是 Jaxen 的 NamespaceContext 接口的默認實現類。
最后一步就是調用 XPath 對象的 selectNodes() 方法進行查找。并把完整的 Dom4j? Document 對象作為參數傳遞進去。實際上,Document 中的任何結點都可以作為參數。
Jaxen 和 XOM
XOM 是基于簡單的 Java DOM APIs 之上的最新工具,它的設計初衷是提供簡單和易學易用的接口。
try {
Builder builder = new Builder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new XOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( ParsingException e) {
// An error occurred parsing the document
...
}
我們需要創建一個 Builder 對象,從文件系統中讀取“catalog.xml”文件,并創建出與 XOM 相關的 Document 對象。
下一步創建出包含了“前綴-命名空間”映射關系的 HashMap 對象。
我們需要創建一個特定于 XOM 的 XPath 對象:XOMXPath。創建方法是把 XPath 表達式傳遞給構造方法,然后就可以通過 XOM 使用 Jaxen 的 XPath 功能了。
創建完 XPath 對象后,同樣,我們把“前綴-命名空間”的映射表用 SimpleNamespaceContext 對象封裝后,傳遞給 XPath 引擎。
最后調用 XPath 對象的“selectNodes()”方法進行查找,把 XOM Document 對象作為本方法的參數。
Jaxen 和 JDOM
JDOM 是第一個提供簡單的 XML 訪問 API 的工具。
try {
SAXBuilder builder = new SAXBuilder();
Document document = builder.build( "file:catalog.xml");
HashMap map = new HashMap();
map.put( "edx", "http://www.edankert.com/examples/");
XPath xpath = new JDOMXPath( "//edx:cd");
xpath.setNamespaceContext( new SimpleNamespaceContext( map));
List nodes = xpath.selectNodes( document);
...
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
// An error occurred opening the document
...
} catch ( JDOMException e) {
// An error occurred parsing the document
...
}
首先,通過 SAXBuilder 創建了一個特定于 JDom 的 Document 對象。
接著創建一個特定于 JDOM 的 XPath 對象:JDOMXPath。
然后,把“前綴-命名空間”的映射表(HashMap)用 SimpleNamespaceContext 對象封裝起來,傳遞給 XPath 引擎。
最后調用 XPath 對象的“selectNodes()”方法來進行查找,并把 JDOM 的 Document 對象作為本方法的輸入參數。
JAXP XPathFactory
從 1.3 版起, JAXP 還提供了一種在 XML Object Models 上進行查詢的通用機制。
try {
DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware( true);
DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
XPathFactory factory =XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext( new NamespaceContext() {
public String getNamespaceURI(String prefix) {
if ( prefix.equals( "edx")) {
return "http://www.edankert.com/examples/";
} else if ...
...
}
return XPathConstants.NULL_NS_URI;
}
public String getPrefix(String namespaceURI) {
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
return "edx";
} else if ...
...
}
return null;
}
public Iterator getPrefixes(String namespaceURI) {
ArrayList list = new ArrayList();
if ( namespaceURI.equals( "http://www.edankert.com/examples/")) {
list.add( "edx");
} else if ...
...
}
return list.iterator();
}
});
Object nodes = xpath.evaluate( "//edx:cd", document.getDocumentElement(),
XPathConstants.NODESET);
...
} catch (ParserConfigurationException e) {
...
} catch (XPathExpressionException e) {
...
} catch (SAXException e) {
...
} catch (IOException e) {
...
}
首先用 JAXP 的 DocumentBuilderFactory 創建一個org.w3c.dom.Document 對象,確保啟用了 namespace 處理功能。
現在可以通過 XPathFactory 來創建 XPath 對象,并通過 XPath 對象對文檔進行查詢。
為了創建“前綴-命名空間”映射并傳遞給 XPath 引擎,我們需要實現 NamespaceContext 接口,該接口目前還沒有默認實現類。這就意味著要實現 getNamespaceURI、getPrefix 和getPrefixes 方法,并確保這些方法能返回正確的值,包括“xmlns”和“xml”前綴所對應的命名空間的 URI 值。
把我們自己實現的 NamespaceContext 對象傳遞給 XPath 引擎后,就可以通過 evaluate 方法來查詢 XPath 表達式所對應的元素:使用上文中提到的 XPath 表達式,并使用 Document 的根節點作為輸入入參數,并接收一個 NodeList 對象作為返回結果。
XSLT
XPath 設計的初衷是用于 XSLT。這也許能解釋“為什么在 XSLT 中定義命名空間的前綴是一件很平常的事”(也許因為 XSLT 也是一個 XML 名詞的緣故吧)。
只需要使用 XML 本身的機制,簡單地為 edx 前綴賦予一個命名空間的 URI 值。
通過與我們的 XPath 表達式“//edx:cd”相匹配的 xsl:template,能得到與上文其他例子相同的輸出結果。
結束語
為了在(缺省)命名空間上使用 XPath 表達式,我們需要指定一個“前綴-命名空間”映射。正如我們所看到的,具體使用什么樣的前綴名稱,是無關緊要的。
同樣的方法,也可以用于查詢那些用其他前綴修飾的元素。這意味著上面的例子對下述 XML 也有效。下述 XML 沒有使用缺省命名空間,而是使用了 examples 作命名空間的前綴:
Sufjan Stevens
Illinoishttp://www.sufjan.com/
Stoat
Future come and get mehttp://www.stoatmusic.com/
The White Stripes
Get behind me satanhttp://www.whitestripes.com/
使用“//edx:cd”作為 XPath 表達式,使用與前文例子相同的“前綴-命名空間”映射,在這個 XML 上同樣能查詢出屬于“http://www.edankert.com/examples/”命名空間的所有“cd”元素。
總結
以上是生活随笔為你收集整理的java xpath 命名空间_【转】玩转 XPath 和缺省命名空间(Default Namespaces)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java rect 旋转_处理(Java
- 下一篇: java 球面距离_[置顶] C语言实验