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


    dom4j的XPath不生效问题

    dom4j使用XPath的时候,发现不能获取到节点

    最可能是命名空间的问题,因为如果节点上有Namespace,那么XPath中就应该使用namespace的前缀。

    例如:

    <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
      <process isExecutable="true">process>
    definitions>
    
    • 1
    • 2
    • 3
    @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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的代码读到的就是空节点

    怎么处理呢?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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这种方式比较简单粗暴,但是去除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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    @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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    位置表达式

    表达式说明
    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    关系表达式

    表达式结果
    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    cmake 命令
    vxe-table表格校验失败后保持可以编辑状态
    干货丨小米自研主动降噪技术在Redmi Buds Pro4上的应用
    c++万能开头<bits/stdc++.h>
    【虚幻引擎UE】UE5 两种球体绘制方法
    性能测试 —— Jmeter 命令行详细
    redis操作五种数据类型
    qmt量化交易策略小白学习笔记第51期【qmt编程之期货列表--国债期货合约表】
    走进苏州的开源创新之旅:开放原子开源大赛苏州站系列活动启幕
    产品工作流| B端产品竞品分析
  • 原文地址:https://blog.csdn.net/trayvontang/article/details/134500460