dom4j基本使用与XPath不生效处理

dom4j的XPath不生效问题

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中方式

  1. XPath表达式中加上对应的前缀
  2. 去除xml字符串中的namespace
  3. 通过dom4j的VisitorSupport去除Namespace节点

使用XPath表达式前缀

@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中设置的一致即可。

改方式的问题是:

  1. 不同节点可能有不同的Namespace,就要每个节点都要处理
  2. ns前缀是所有节点都要加,而不是整个表达式前面就可以

去除xml字符串中的namespace

@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中的命名空间的表达式需要兼容性好。

通过VisitorSupport去除Namespace节点

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);
}

XPath表达式

基本表达式

表达式描述
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());
    }
}

Element常用方法与说明

方法说明
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());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值