【问题标题】:Get XPath of XML Tag获取 XML 标签的 XPath
【发布时间】:2013-01-22 22:34:18
【问题描述】:

如果我有如下的 XML 文档:

<foo>
   <foo1>Foo Test 1</foo1>
   <foo2>
       <another1>
           <test10>This is a duplicate</test10>
       </another1>
   </foo2>
   <foo2>
       <another1>
           <test1>Foo Test 2</test1>
       </another1>
   </foo2>
   <foo3>Foo Test 3</foo3>
   <foo4>Foo Test 4</foo4>
</foo>

例如,我如何获得&lt;test1&gt; 的XPath?所以 输出 应该是这样的:foo/foo2[2]/another1/test1

我猜代码看起来像这样:

public String getXPath(Document document, String xmlTag) {
   String xpath = "";
   ...
   //Get the node from xmlTag
   //Get the xpath using the node
   return xpath;
}

假设String XPathVar = getXPath(document, "&lt;test1&gt;");。我需要取回一个可以在以下代码中工作的绝对 xpath:

XPath xpath = XPathFactory.newInstance().newXPath(); 
XPathExpression xpr = xpath.compile(XPathVar); 
xpr.evaluate(Document, XPathConstants.STRING); 

但它不能像//test1 这样的快捷方式,因为它也将用于元数据目的。

通过以下方式打印结果时:

System.out.println(xpr.evaluate(Document, XPathConstants.STRING));

我应该得到节点的值。所以如果XPathVar = foo/foo2[2]/another1/test1那么我应该回来:

Foo Test 2 而不是This is a duplicate

【问题讨论】:

  • return "//" + xmlTag 怎么样?
  • @ThreaT 你使用xpath从文档中获取节点,如果你一开始就有节点,为什么要把节点变成只能用来再次获取节点的xpath ?
  • return "(//" + xmlTag + ")[1]"; 怎么样?
  • 那个节点在XML文档中的绝对路径。你是说你需要完整的路径,包括所有的父节点?如果是这样,你为什么需要它?

标签: java xml xslt xpath


【解决方案1】:

你不会像没有'得到' sql 一样'得到' xpath。

xpath 是您根据对 xml 文档或架构的理解编写的查询,就像 sql 是您根据对数据库架构的理解编写的查询一样 - 您不会“获得”其中任何一个。

我可以从 DOM 生成 xpath 语句,只需从给定节点返回节点即可,但一般而言,考虑到每个节点上的属性值,这样做会使生成的代码几乎无用.例如(带有警告,这将找到具有给定名称的第一个节点,xpath 远不止于此,您也可以使用 xpath //foo2):

import java.io.ByteArrayInputStream;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class XPathExample 
{
  private static String getXPath(Node root, String elementName)
  {
    for (int i = 0; i < root.getChildNodes().getLength(); i++)
    {
      Node node = root.getChildNodes().item(i);

      if (node instanceof Element)
      {
        if (node.getNodeName().equals(elementName))
        {
          return "/" + node.getNodeName();
        }
        else if (node.getChildNodes().getLength() > 0)
        {
          String xpath = getXPath(node, elementName);
          if (xpath != null)
          {
            return "/" + node.getNodeName() + xpath;
          }
        }
      }
    }

    return null;
  }

  private static String getXPath(Document document, String elementName)
  {
    return document.getDocumentElement().getNodeName() + getXPath(document.getDocumentElement(), elementName);
  }

  public static void main(String[] args) 
  {
    try 
    {
      Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
        new ByteArrayInputStream(
          ("<foo><foo1>Foo Test 1</foo1><foo2><another1><test1>Foo Test 2</test1></another1></foo2><foo3>Foo Test 3</foo3><foo4>Foo Test 4</foo4></foo>").getBytes()
        )
      );

      String xpath = "/" + getXPath(document, "test1");
      System.out.println(xpath);        

      Node node1 = (Node)XPathFactory.newInstance().newXPath().compile(xpath).evaluate(document, XPathConstants.NODE);
      Node node2 = (Node)XPathFactory.newInstance().newXPath().compile("//test1").evaluate(document, XPathConstants.NODE);

      //This evaluates to true, hence you may as well just use the xpath //test1.
      System.out.println(node1.equals(node2));
    }
    catch (Exception e) 
    {
      e.printStackTrace();
    }
  }
}

同样,您可以编写一个 XML 转换,将 xml 文档转换为一系列 xpath 语句,但这种转换会比一开始编写 xpath 更复杂,而且基本上没有意义。

【讨论】:

  • @ThreaT 添加了一个示例,但正如我在文中所说 - 这将找到第一个具有给定名称的节点,xpath 远不止于此,您也可以使用 xpath @ 987654324@如果这是你想做的。
  • 在问题//test1 的编辑示例中,将找到test1 节点。 // 表示从当前节点中选择文档中与选择匹配的节点,无论它们在哪里 - 请参阅 w3schools.com/xpath/xpath_syntax.asp
  • 对不起,示例调用应该是String xpath = "//" + getXPath(document.getDocumentElement(), "foo2"));
  • 是的,但我的意思是 xpath 是达到目的的一种手段——你用它来获取一个节点。以代码示例生成 xpath /foo/foo2/another1/test1 的一般方式解析 DOM 与使用 //test1 并传递 QName XPathConstants.NODE 相同。
  • 当我运行它生成的代码时 - /foo2/another1/test1 - 我已经用我正在运行的完整示例替换了代码。
【解决方案2】:

这是怎么回事:

private static String getXPath(Document root, String elementName)
{
  try{
      XPathExpression expr = XPathFactory.newInstance().newXPath().compile("//" + elementName);
      Node node = (Node)expr.evaluate(root, XPathConstants.NODE);

      if(node != null) {
          return getXPath(node);
      }
  }
  catch(XPathExpressionException e) { }

  return null;
}

private static String getXPath(Node node) {
    if(node == null || node.getNodeType() != Node.ELEMENT_NODE) {
        return "";
    }

    return getXPath(node.getParentNode()) + "/" + node.getNodeName();
}

请注意,这是首先定位节点(使用 XPath),然后使用定位的节点来获取其 XPath。相当迂回的方法来获得你已经拥有的价值。

工作ideone示例:http://ideone.com/EL4783

【讨论】:

  • 找到并修复了其中的四个。现在怎么样了?
  • 刚刚添加了指向ideone的链接。
  • 您能否解释一下为什么您需要这些特定的 XPath? (//test1)[1] 定位文档中的第一个 test1,因此如果您必须拥有这些特定的 XPath,至少告诉我们原因。你让我们为你做作业吗?
  • 好的,那么如果存在歧义,XPath 是否应该仅包含 [1],基于源 XML,还是应该在路径的每个步骤中包含 [1]?
  • 这根本不能回答我的问题。您刚刚告诉我输出不是“100% 正确”,因为它不包含 [x]es,那么您希望如何处理它们?我的函数的当前输出使用xpr.evaluate(Document, XPathConstants.STRING);定位第一个匹配节点,因此如果您有特定要求,请定义它们。自从您昨天发布问题以来,您还没有这样做过一次。
猜你喜欢
  • 1970-01-01
  • 2015-03-15
  • 1970-01-01
  • 2014-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-24
  • 1970-01-01
相关资源
最近更新 更多