XPath、XML名称空间和Java
我花了一天的时间试图从下面的文档中提取出一个XML节点,但我无法理解XML名称空间的细微差别以使其正常工作 XML文件太大,无法全部发布,因此下面是与我有关的部分:XPath、XML名称空间和Java,java,xpath,xml-namespaces,xfdl,Java,Xpath,Xml Namespaces,Xfdl,我花了一天的时间试图从下面的文档中提取出一个XML节点,但我无法理解XML名称空间的细微差别以使其正常工作 XML文件太大,无法全部发布,因此下面是与我有关的部分: <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <XFDL xmlns="http://www.PureEdge.com/XFDL/6.5" xmlns:custom="http://www.PureEdge.com/XFDL/Custom"
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<XFDL xmlns="http://www.PureEdge.com/XFDL/6.5" xmlns:custom="http://www.PureEdge.com/XFDL/Custom" xmlns:designer="http://www.PureEdge.com/Designer/6.1" xmlns:pecs="http://www.PureEdge.com/PECustomerService" xmlns:xfdl="http://www.PureEdge.com/XFDL/6.5">
<globalpage sid="global">
<global sid="global">
<xmlmodel xmlns:xforms="http://www.w3.org/2003/xforms">
<instances>
<xforms:instance id="metadata">
<form_metadata>
<metadataver version="1.0"/>
<metadataverdate>
<date day="05" month="Jul" year="2005"/>
</metadataverdate>
<title>
<documentnbr number="2062" prefix.army="DA" scope="army" suffix=""/>
<longtitle>HAND RECEIPT/ANNEX NUMBER </longtitle>
</title>
其中QUERY_FORM_NUMBER是我的XPath表达式,XFDLNamespaceContext实现NamespaceContext,如下所示:
/***
* Locates the Document Number information in the file and returns the form number.
* @return File's self-declared number.
* @throws InvalidFormException Thrown when XPath cannot find the "documentnbr" element in the file.
*/
public String getFormNumber() throws InvalidFormException
{
try{
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new XFDLNamespaceContext());
Node result = (Node)xPath.evaluate(QUERY_FORM_NUMBER, doc, XPathConstants.NODE);
if(result != null) {
return result.getNodeValue();
} else {
throw new InvalidFormException("Unable to identify form.");
}
} catch (XPathExpressionException err) {
throw new InvalidFormException("Unable to find form number in file.");
}
}
public class XFDLNamespaceContext implements NamespaceContext {
@Override
public String getNamespaceURI(String prefix) {
if (prefix == null) throw new NullPointerException("Invalid Namespace Prefix");
else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))
return "http://www.PureEdge.com/XFDL/6.5";
else if ("custom".equals(prefix))
return "http://www.PureEdge.com/XFDL/Custom";
else if ("designer".equals(prefix))
return "http://www.PureEdge.com/Designer/6.1";
else if ("pecs".equals(prefix))
return "http://www.PureEdge.com/PECustomerService";
else if ("xfdl".equals(prefix))
return "http://www.PureEdge.com/XFDL/6.5";
else if ("xforms".equals(prefix))
return "http://www.w3.org/2003/xforms";
else
return XMLConstants.NULL_NS_URI;
}
@Override
public String getPrefix(String arg0) {
// TODO Auto-generated method stub
return null;
}
@Override
public Iterator getPrefixes(String arg0) {
// TODO Auto-generated method stub
return null;
}
}
我尝试了许多不同的XPath查询,但我一直觉得这样应该可以:
protected static final String QUERY_FORM_NUMBER =
"/globalpage/global/xmlmodel/xforms:instances/instance" +
"/form_metadata/title/documentnbr[number]";
不幸的是,它不起作用,我不断得到一个空返回
我已经读了相当多的书,但是没有什么能充分说明我的工作
我几乎可以肯定,当我明白这一点的时候,我会直面现实,但我真的不知道我错过了什么
感谢您通读所有这些内容,并提前感谢您的帮助
-安迪啊哈,我试着调试你的表情,让它起作用了。你错过了一些东西。此XPath表达式应该完成以下操作:
/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number
instance
替换为xforms:instance
,则会使用xforms
作为输入参数调用getNamespaceURI(),但程序会引发异常@attr
,而不是[attr]
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
public class XPathNamespaceExample {
static public class MyNamespaceContext implements NamespaceContext {
final private Map<String, String> prefixMap;
MyNamespaceContext(Map<String, String> prefixMap)
{
if (prefixMap != null)
{
this.prefixMap = Collections.unmodifiableMap(new HashMap<String, String>(prefixMap));
}
else
{
this.prefixMap = Collections.emptyMap();
}
}
public String getPrefix(String namespaceURI) {
// TODO Auto-generated method stub
return null;
}
public Iterator getPrefixes(String namespaceURI) {
// TODO Auto-generated method stub
return null;
}
public String getNamespaceURI(String prefix) {
if (prefix == null) throw new NullPointerException("Invalid Namespace Prefix");
else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX))
return "http://www.PureEdge.com/XFDL/6.5";
else if ("custom".equals(prefix))
return "http://www.PureEdge.com/XFDL/Custom";
else if ("designer".equals(prefix))
return "http://www.PureEdge.com/Designer/6.1";
else if ("pecs".equals(prefix))
return "http://www.PureEdge.com/PECustomerService";
else if ("xfdl".equals(prefix))
return "http://www.PureEdge.com/XFDL/6.5";
else if ("xforms".equals(prefix))
return "http://www.w3.org/2003/xforms";
else
return XMLConstants.NULL_NS_URI;
}
}
protected static final String QUERY_FORM_NUMBER =
"/XFDL/globalpage/global/xmlmodel/xforms:instances/instance" +
"/form_metadata/title/documentnbr[number]";
public static void main(String[] args) {
try
{
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
Document doc = docBuilder.parse(new File(args[0]));
System.out.println(extractNodeValue(doc, "/XFDL/globalpage/@sid"));
System.out.println(extractNodeValue(doc, "/XFDL/globalpage/global/xmlmodel/instances/instance/@id" ));
System.out.println(extractNodeValue(doc, "/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number" ));
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
}
private static String extractNodeValue(Document doc, String expression) {
try{
XPath xPath = XPathFactory.newInstance().newXPath();
xPath.setNamespaceContext(new MyNamespaceContext(null));
Node result = (Node)xPath.evaluate(expression, doc, XPathConstants.NODE);
if(result != null) {
return result.getNodeValue();
} else {
throw new RuntimeException("can't find expression");
}
} catch (XPathExpressionException err) {
throw new RuntimeException(err);
}
}
}
XPath xpath = XPathFactory.newInstance().newXPath();
NamespaceContextMap contextMap = new NamespaceContextMap();
contextMap.put("custom", "http://www.PureEdge.com/XFDL/Custom");
contextMap.put("designer", "http://www.PureEdge.com/Designer/6.1");
contextMap.put("pecs", "http://www.PureEdge.com/PECustomerService");
contextMap.put("xfdl", "http://www.PureEdge.com/XFDL/6.5");
contextMap.put("xforms", "http://www.w3.org/2003/xforms");
contextMap.put("", "http://www.PureEdge.com/XFDL/6.5");
xpath.setNamespaceContext(contextMap);
String expression = "//:documentnbr/@number";
InputSource inputSource = new InputSource("input.xml");
String number;
number = (String) xpath.evaluate(expression, inputSource, XPathConstants.STRING);
System.out.println(number);
导入java.io.File;
导入java.io.IOException;
导入java.util.Collections;
导入java.util.HashMap;
导入java.util.Iterator;
导入java.util.Map;
导入javax.xml.xmlstants;
导入javax.xml.namespace.NamespaceContext;
导入javax.xml.parsers.DocumentBuilder;
导入javax.xml.parsers.DocumentBuilderFactory;
导入javax.xml.parsers.parserConfiguration异常;
导入javax.xml.xpath.xpath;
导入javax.xml.xpath.XPathConstants;
导入javax.xml.xpath.XPathExpressionException;
导入javax.xml.xpath.XPathFactory;
导入org.w3c.dom.Document;
导入org.w3c.dom.Node;
导入org.xml.sax.SAXException;
公共类XPathNamespaceExample{
静态公共类MyNamespaceContext实现NamespaceContext{
最终私有映射前缀映射;
MyNamespaceContext(映射前缀映射)
{
if(prefixMap!=null)
{
this.prefixMap=Collections.unmodifiableMap(新HashMap(prefixMap));
}
其他的
{
this.prefixMap=Collections.emptyMap();
}
}
公共字符串getPrefix(字符串名称空间URI){
//TODO自动生成的方法存根
返回null;
}
公共迭代器getPrefixes(字符串名称空间URI){
//TODO自动生成的方法存根
返回null;
}
公共字符串getNamespaceURI(字符串前缀){
if(prefix==null)抛出新的NullPointerException(“无效的命名空间前缀”);
else if(前缀.equals(XMLConstants.DEFAULT\u NS\u前缀))
返回“http://www.PureEdge.com/XFDL/6.5";
else if(“自定义”。等于(前缀))
返回“http://www.PureEdge.com/XFDL/Custom";
else if(“designer”.equals(前缀))
返回“http://www.PureEdge.com/Designer/6.1";
else if(“pecs.”等于(前缀))
返回“http://www.PureEdge.com/PECustomerService";
else if(“xfdl”.equals(前缀))
返回“http://www.PureEdge.com/XFDL/6.5";
else if(“xforms”.equals(前缀))
返回“http://www.w3.org/2003/xforms";
其他的
返回XMLConstants.NULL\u NS\u URI;
}
}
受保护的静态最终字符串查询\u表单\u编号=
“/XFDL/globalpage/global/xmlmodel/xforms:instances/instance”+
“/form_metadata/title/documentnbr[编号]”;
公共静态void main(字符串[]args){
尝试
{
DocumentBuilderFactory dbfac=DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder=dbfac.newDocumentBuilder();
Document doc=docBuilder.parse(新文件(args[0]);
System.out.println(extractNodeValue(doc,“/XFDL/globalpage/@sid”);
println(extractNodeValue(doc,“/XFDL/globalpage/global/xmlmodel/instances/instance/@id”);
System.out.println(extractNodeValue(doc,“/XFDL/globalpage/global/xmlmodel/instances/instance/form_metadata/title/documentnbr/@number”);
}捕获(SAXE异常){
e、 printStackTrace();
}捕获(IOE异常){
e、 printStackTrace();
}捕获(ParserConfiguration异常e){
e、 printStackTrace();
}
}
私有静态字符串extractNodeValue(文档文档,字符串表达式){
试一试{
XPath=XPathFactory.newInstance().newXPath();
setNamespaceContext(新的MyNamespaceContext(null));
节点结果=(节点)xPath.evaluate(表达式、文档、XPathConstants.Node);
如果(结果!=null){
返回result.getNodeValue();
}否则{
抛出新的RuntimeException(“找不到表达式”);
}
}捕获(XPathExpressionException错误){
抛出新的运行时异常(err);
}
}
}
SAX(XPath的替代品)版本:
我认为将XPath与名称空间结合使用会更加复杂,因为它应该是(我的观点)。以下是我的(简单)代码:
您可以从(GPL许可证)获取NamespaceContextMap类(不是我的)。还有一个bug。看看这个库。这是一种更简单的使用XPath的方法,而不会与低级Java API发生冲突,
XPath xpath = XPathFactory.newInstance().newXPath();
NamespaceContextMap contextMap = new NamespaceContextMap();
contextMap.put("custom", "http://www.PureEdge.com/XFDL/Custom");
contextMap.put("designer", "http://www.PureEdge.com/Designer/6.1");
contextMap.put("pecs", "http://www.PureEdge.com/PECustomerService");
contextMap.put("xfdl", "http://www.PureEdge.com/XFDL/6.5");
contextMap.put("xforms", "http://www.w3.org/2003/xforms");
contextMap.put("", "http://www.PureEdge.com/XFDL/6.5");
xpath.setNamespaceContext(contextMap);
String expression = "//:documentnbr/@number";
InputSource inputSource = new InputSource("input.xml");
String number;
number = (String) xpath.evaluate(expression, inputSource, XPathConstants.STRING);
System.out.println(number);
String num = XPathAPI.selectSingleNodeAsString(doc, '//documentnbr/@number');
Map<String, String> nsMap = new HashMap<String, String>();
nsMap.put("xforms", "http://www.w3.org/2003/xforms");
String num =
XPathAPI.selectSingleNodeAsString(doc, '//documentnbr/@number', nsMap);