dom4j使用XPath的时候,发现不能获取到节点
最可能是命名空间的问题,因为如果节点上有Namespace,那么XPath中就应该使用namespace的前缀。
例如:
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process isExecutable="true">process>
definitions>
@Test
public void base() throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("F:\\tmp\\camunda.xml"));
Node versionNode = document.selectSingleNode("/definitions/process");
System.out.println(versionNode);
}
上面的代码读到的就是空节点
怎么处理呢?3中方式
@Test
public void prefixNamespace() throws DocumentException {
SAXReader reader = new SAXReader();
Map<String, String> map = new HashMap<>();
reader.getDocumentFactory().setXPathNamespaceURIs(map);
File file = new File("F:\\tmp\\camunda.xml");
Document document = reader.read(file);
String namespaceURI = document.getRootElement().getNamespaceURI();
map.put("ns", namespaceURI);
XPath xPath = document.createXPath("/ns:definitions/ns:process");
System.out.println(xPath.selectSingleNode(document));
}
XPath表达式"/ns:definitions/ns:process"中的ns就是前缀,其中ns是自定义的,可以随便改和map中设置的一致即可。
改方式的问题是:
@Test
public void removeStringNamespace() throws DocumentException, IOException {
SAXReader reader = new SAXReader();
Path path = Paths.get("F:\\tmp\\camunda.xml");
String xml = Files.readString(path);
xml = xml .replaceAll("xmlns=\"[^\"]*\"","");
System.out.println(xml);
StringReader stringReader = new StringReader(xml);
Document document = reader.read(stringReader);
Node node = document.selectSingleNode("/definitions/process");
System.out.println(node);
}
这种方式比较简单粗暴,但是去除xml中的命名空间的表达式需要兼容性好。
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.VisitorSupport;
import org.dom4j.tree.DefaultElement;
/**
* dom4j xpath 清理namespace
*/
public final class Dom4jNameSpaceCleaner extends VisitorSupport {
public void visit(Document document) {
((DefaultElement) document.getRootElement())
.setNamespace(Namespace.NO_NAMESPACE);
document.getRootElement().additionalNamespaces().clear();
}
public void visit(Namespace namespace) {
namespace.detach();
}
public void visit(Attribute node) {
String content = node.toString();
if (content.contains("xmlns")
|| content.contains("xsi:")) {
node.detach();
}
}
public void visit(Element node) {
if (node instanceof DefaultElement) {
((DefaultElement) node).setNamespace(Namespace.NO_NAMESPACE);
}
}
}
@Test
public void dom4jNameSpaceCleaner() throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("F:\\tmp\\camunda.xml"));
document.accept(new Dom4jNameSpaceCleaner());
Node node = document.selectSingleNode("/definitions/process");
System.out.println(node);
}
表达式 | 描述 |
---|---|
nodeName | 获取当前节点的所有nodeName节点,不包含孙节点 |
/nodeName | 从根节点开始匹配,就是根节点下的nodeName节点 |
//nodeName | 获取所有nodeName节点,不考虑它们的位置 |
. | 获取当前节点 |
… | 获取当前节点的父节点 |
@ | 获取属性 |
* | 匹配任何节点节点 |
@* | 匹配所有属性 |
/process/* | 获取process所有子节点 |
//* | 获取文档中的所有节点 |
//userTask[@id] | 获取所有id属性的userTask节点 |
//userTask/incoming | 获取userTask节点下的incoming节点 |
//incoming | //outgoing |
@Test
public void xpath() throws DocumentException, IOException {
String xmlFileStr = "F:\\tmp\\camunda.xml";
Document document = Dom4jXmlHelper.getDocument(xmlFileStr);
Node process = document.selectSingleNode("/definitions/process");
// 获取process下的所有userTask节点
List<Node> nodes = process.selectNodes("userTask");
for(Node node : nodes){
System.out.println(node.getName());
}
// 获取process下的所有userTask节点下的incoming节点
nodes = process.selectNodes("userTask/incoming");
for(Node node : nodes){
System.out.println(node.getName());
}
// 获取process下的所有userTask节点下的camunda:前缀节点,不考虑位置
nodes = process.selectNodes("userTask//camunda:*");
System.out.println("camunda:" + nodes.size());
for(Node node : nodes){
System.out.println(node.getName());
}
// 获取process下的所有id属性
nodes = process.selectNodes("//@id");
for(Node node : nodes){
System.out.println(node.getStringValue());
}
// 获取process下的userTask节点下所有属性
nodes = process.selectNodes("userTask//@*");
for(Node node : nodes){
System.out.println(node.getStringValue());
}
}
表达式 | 说明 |
---|---|
process/userTask[1]/@id | 获取当前节点下的process节点下的第1个userTask节点的id属性 |
process/userTask[last()] | 获取最后一个userTask节点 |
process/userTask[last()-1] | 获取倒数第2个userTask节点 |
process/userTask[position()❤️] | 获取前2个userTask节点 |
//userTask[@id] | 获取有id属性的 userTask节点 |
//userTask[@id=‘Activity_0ut6bzp’] | 获取属性id为Activity_0ut6bzp的userTask节点 |
process/userTask[camunda>10] | 获取当前节点process节点下的userTask节点下的camunda节点值大于10的节点 |
process/userTask[camunda>10]/string | 上一个的string子节点 |
@Test
public void xpathPosition() throws DocumentException, IOException {
String xmlFileStr = "F:\\tmp\\camunda.xml";
Document document = Dom4jXmlHelper.getDocument(xmlFileStr);
Element root = document.getRootElement();
List<Node> nodes = root.selectNodes("process/userTask[1]/@id");
for (Node node : nodes) {
System.out.println(node.getStringValue());
}
nodes = root.selectNodes("process/userTask[last()]/@id");
for (Node node : nodes) {
System.out.println(node.getStringValue());
}
}
表达式 | 结果 |
---|---|
self | 获取当前节点 |
parent | 获取当前节点的父节点 |
child | 获取当前节点的所有子节点 |
ancestor | 获取当前节点的所有先辈 |
ancestor-or-self | 获取当前节点的所有先辈以及当前节点本身 |
attribute | 获取当前节点的所有属性 |
descendant | 获取当前节点的所有后代节点 |
descendant-or-self | 获取当前节点的所有后代节点(子、孙等)以及当前节点本身 |
following | 获取文档中当前节点的结束标签之后的所有节点 |
namespace | 获取当前节点的所有命名空间节点 |
preceding | 获取文档中当前节点的开始标签之前的所有节点 |
preceding-sibling | 获取当前节点之前的所有同级节点 |
child::userTask | 获取所有属于当前节点的子节点的userTask节点 |
attribute::id | 获取当前节点的id属性 |
attribute: | 获取当前节点的所有属性 |
child: | 获取当前节点的所有子节点 |
child::text() | 获取当前节点的所有文本子节点 |
child::node() | 获取当前节点的所有子节点 |
descendant::userTask | 获取当前节点的所有 userTask 后辈 |
ancestor::userTask | 选择当前节点的所有 userTask 先辈 |
ancestor-or-self::userTask | 获取当前节点的所有userTask先辈以及当前节点 |
child:/child::userTask | 获取当前节点的所有 userTask 孙节点 |
@Test
public void xpathRelation() throws DocumentException, IOException {
String xmlFileStr = "F:\\tmp\\camunda.xml";
Document document = Dom4jXmlHelper.getDocument(xmlFileStr);
Element root = document.getRootElement();
List<Node> nodes = root.selectNodes("process/attribute::*");
for (Node node : nodes) {
System.out.println(node.getStringValue());
}
}
方法 | 说明 |
---|---|
getNamespace() | 节点所属的Namespace对象 |
getNamespacePrefix() | 节点所属的Namespace对象的prefix |
getNamespaceURI() | 节点所属的Namespace对象的URI |
getName() | 节点的local name |
getQualifiedName() | 节点的qualified name |
getText() | 节点所含有的text内容,如果内容为空则返回一个空字符串而不是null |
getTextTrim() | 节点所含有的text内容,其中连续的空格被转化为单个空格,该方法不会返回null |
attributeIterator() | 节点属性的iterator,其中每个节点都是Attribute对象 |
attributeValue() | 节点的某个指定属性所含的值 |
elementIterator() | 节点的子节点的iterator,其中每个节点都是Element对象 |
element(name) | 获取指定名称第1个子节点节点 |
elements(name) | 获取指定名称所有子节点节点 |
elementText() | 节点的的text |
getParent | 获取父节点 |
getPath() | 节点的XPath表达式 |
isTextOnly() | 是否该节点只含有text或是空节点 |
isRootElement() | 是否该节点是XML树的根节点 |
使用非XPath方式,就得手动通过API去一层一层的查找。
@Test
public void base() throws DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("F:\\tmp\\camunda.xml"));
Element rootElement = document.getRootElement();
Element process = rootElement.element("process");
List<Element> elementList = process.elements("userTask");
System.out.println(elementList.size());
for(Element element : elementList){
System.out.println(element.attribute("id").getValue());
}
}