【问题标题】:How to match string that ends with a number using XPath如何使用 XPath 匹配以数字结尾的字符串
【发布时间】:2019-03-15 12:20:53
【问题描述】:

问题是我正在寻找构建一个 XPath 表达式来获取具有属性 XXX 的节点,这些节点具有像 TP* 这样的值,其中星号是一个数字。 假设我有这个 XML 文件

<tagA attA="VAL1">text</tagA>
<tagB attB="VAL333">text</tagB>
<tagA attA="VAL2">text</tagA>
<tagA attA="V2">text</tagA>

所以 xpath 表达式应该让我所有 tagA 具有属性 attrA 与模式 VAL*
//tagA[@attrA[matches('VAL\d')]]: 不工作

【问题讨论】:

    标签: java xml xpath java-8


    【解决方案1】:

    如果您需要 XPath 1.0 解决方案,请尝试以下操作:

    //tagA[boolean(number(substring-after(@attA, "VAL"))) or number(substring-after(@attA, "VAL")) = 0]
    

    如果@attA 不能是"VAL0",那么只需

    //tagA[boolean(number(substring-after(@attA, "VAL")))]
    

    【讨论】:

    • 嗨@Andersson,谢谢你的回答。我现在就测试一下。
    【解决方案2】:

    matches() 需要 XPath 2.0,但 Java 8 中的 javax.xml.xpath 仅支持 XPath 1.0。

    此外,matches() 的第一个参数是要匹配的字符串。所以,你会想要:

    //tagA[@attrA[matches(., 'VAL\d')]]
    

    这是寻找"VAL" 加上@attrA 属性值中任意位置的一个数字。如果您希望将整个字符串与多个/可选数字后缀 (XPath 2.0) 匹配,请参阅 @jschnasse's answer 中的正则表达式,或者参阅 Andersson's answer 以获得 XPath 1.0 解决方案。

    【讨论】:

      【解决方案3】:

      将量词 (*,+,...) 添加到您的 \d。试试

      '^VAL\d*$'
      

      正如@kjhughes 所指出的。这不适用于标准 Java,因为即使是当前版本的 Java 11 也不支持 XPath 2.0。 但是,如果您需要 XPath 2.0 支持,您可以使用 Saxon

      撒克逊示例(这是一个variant of this answer using javax.xml

      Processor processor = new Processor(false);
      
      @Test
      public void xpathWithSaxon() {
          String xml = "<root><tagA attA=\"VAL1\">text</tagA>\n" + "<tagB attB=\"VAL333\">text</tagB>\n"
                          + "<tagA attA=\"VAL2\">text</tagA>\n" + "<tagA attA=\"V2\">text</tagA>\n" + "</root>";
          try (InputStream in = new ByteArrayInputStream(xml.getBytes("utf-8"));) {
              processFilteredXmlWith(in, "//root/tagA[matches(@attA,'^VAL\\d*$')]", (node) -> {
                  printItem(node, System.out);
              });
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
      
      private void printItem(XdmItem node, PrintStream out) {
          out.println(node);
      }
      
      public void processFilteredXmlWith(InputStream in, String xpath, Consumer<XdmItem> process) {
          XdmNode doc = readXmlWith(in);
          XdmValue list = filterNodesByXPathWith(doc, xpath);
          list.forEach((node) -> {
              process.accept(node);
          });
      
      }
      
      private XdmNode readXmlWith(InputStream xmlin) {
          try {
              return processor.newDocumentBuilder().build(new StreamSource(xmlin));
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
      
      private XdmValue filterNodesByXPathWith(XdmNode doc, String xpathExpr) {
          try {
              return processor.newXPathCompiler().evaluate(xpathExpr, doc);
          } catch (Exception e) {
              throw new RuntimeException(e);
          }
      }
      

      打印

      <tagA attA="VAL1">text</tagA>
      
      <tagA attA="VAL2">text</tagA>
      

      【讨论】:

      • +1 关于正则表达式调整,但也有additional issues
      • 谢谢。因为仍然不支持 XPath 2.0 (Java 11)。我发现发布一个基于 Saxon 的解决方案是公平的,它允许通过正则表达式过滤属性。
      猜你喜欢
      • 1970-01-01
      • 2015-08-01
      • 2012-09-13
      • 2014-03-03
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 2017-04-26
      相关资源
      最近更新 更多