Подтвердить что ты не робот

Получить Xpath из org.w3c.dom.Node

Можно ли получить полный xpath из org.w3c.dom.Node?

Скажите, что в настоящее время node указывает на то, где находится середина xml-документа. Я хотел бы извлечь xpath для этого элемента.

Выход xpath, который я ищу, - //parent/child1/chiild2/child3/node. Родительский путь к node xpath. Просто игнорируйте xpath, который имеет выражения и указывает на тот же node.

4b9b3361

Ответ 1

Нет никакого общего метода для получения XPath, главным образом потому, что нет единого универсального XPath, который идентифицирует конкретный node в документе. В некоторых схемах узлы будут идентифицироваться уникальным атрибутом (id и name, вероятно, являются наиболее распространенными атрибутами.) В других именах каждого элемента (т.е. Тега) достаточно, чтобы однозначно идентифицировать node. В нескольких (маловероятных, но возможных) случаях нет ни одного уникального имени или атрибута, который приведет вас к определенному node, и поэтому вам нужно будет использовать мощность (получите n-й дочерний элемент m-го ребенка из...).

ИЗМЕНИТЬ: В большинстве случаев нетрудно создать зависимую от схемы функцию для сборки XPath для заданного node. Например, предположим, что у вас есть документ, где каждый node уникально идентифицируется атрибутом id, и вы не используете пространства имен. Затем (я думаю) следующая псевдо-Java будет работать, чтобы вернуть XPath на основе этих атрибутов. (Предупреждение: я не тестировал это.)

String getXPath(Node node)
{
    Node parent = node.getParent();
    if (parent == null) {
        return "/" + node.getTagName();
    }
    return getXPath(parent) + "/" + "[@id='" + node.getAttribute("id") + "']";
}

Ответ 2

Я работаю над компанией jOOX, библиотекой, которая предоставляет множество полезных расширений для стандартного API DOM стандарта Java, имитируя jQuery API. С помощью jOOX вы можете получить XPath любого элемента, подобного этому:

String path = $(element).xpath();

Вышеуказанный путь будет таким, как это.

/document[1]/library[2]/books[3]/book[1]

Ответ 3

Я взял этот код из Mikkel Flindt post и изменил его, чтобы он мог работать для атрибута Node.

public static String getFullXPath(Node n) {
// abort early
if (null == n)
  return null;

// declarations
Node parent = null;
Stack<Node> hierarchy = new Stack<Node>();
StringBuffer buffer = new StringBuffer();

// push element on stack
hierarchy.push(n);

switch (n.getNodeType()) {
case Node.ATTRIBUTE_NODE:
  parent = ((Attr) n).getOwnerElement();
  break;
case Node.ELEMENT_NODE:
  parent = n.getParentNode();
  break;
case Node.DOCUMENT_NODE:
  parent = n.getParentNode();
  break;
default:
  throw new IllegalStateException("Unexpected Node type" + n.getNodeType());
}

while (null != parent && parent.getNodeType() != Node.DOCUMENT_NODE) {
  // push on stack
  hierarchy.push(parent);

  // get parent of parent
  parent = parent.getParentNode();
}

// construct xpath
Object obj = null;
while (!hierarchy.isEmpty() && null != (obj = hierarchy.pop())) {
  Node node = (Node) obj;
  boolean handled = false;

  if (node.getNodeType() == Node.ELEMENT_NODE) {
    Element e = (Element) node;

    // is this the root element?
    if (buffer.length() == 0) {
      // root element - simply append element name
      buffer.append(node.getNodeName());
    } else {
      // child element - append slash and element name
      buffer.append("/");
      buffer.append(node.getNodeName());

      if (node.hasAttributes()) {
        // see if the element has a name or id attribute
        if (e.hasAttribute("id")) {
          // id attribute found - use that
          buffer.append("[@id='" + e.getAttribute("id") + "']");
          handled = true;
        } else if (e.hasAttribute("name")) {
          // name attribute found - use that
          buffer.append("[@name='" + e.getAttribute("name") + "']");
          handled = true;
        }
      }

      if (!handled) {
        // no known attribute we could use - get sibling index
        int prev_siblings = 1;
        Node prev_sibling = node.getPreviousSibling();
        while (null != prev_sibling) {
          if (prev_sibling.getNodeType() == node.getNodeType()) {
            if (prev_sibling.getNodeName().equalsIgnoreCase(
                node.getNodeName())) {
              prev_siblings++;
            }
          }
          prev_sibling = prev_sibling.getPreviousSibling();
        }
        buffer.append("[" + prev_siblings + "]");
      }
    }
  } else if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
    buffer.append("/@");
    buffer.append(node.getNodeName());
  }
}
// return buffer
return buffer.toString();
}          

Ответ 4

Некоторые IDE, специализированные в XML, сделают это для вас.

Вот наиболее известные

Например, в oXygen вы можете щелкнуть правой кнопкой мыши элементную часть документа XML, а в контекстном меню будет опция "Копировать Xpath".

Существует также ряд надстроек Firefox (таких как XPather, которые с радостью сделают эту работу для вас. Для Xpather, вы просто нажимаете на часть веб-страницы и выбираете в контекстном меню "show in XPather", и все готово.

Но, как указал Дэн в своем ответе, выражение XPath будет ограниченным использованием. Например, он не будет включать предикаты. Скорее это будет выглядеть так.

/root/nodeB[2]/subnodeX[2]

Для документа типа

<root>
   <nodeA>stuff</nodeA>
   <nodeB>more stuff</nodeB>
   <nodeB cond="thisOne">
       <subnodeX>useless stuff</subnodeX>
       <subnodeX id="MyCondition">THE STUFF YOU WANT</subnodeX>
       <subnodeX>more useless stuff</subnodeX>
   </nodeB>
</root>

Перечисленные инструменты не будут генерировать

/root/nodeB[@cond='thisOne']/subnodeX[@id='MyCondition']

Например, для html-страницы вы получите довольно бесполезное выражение:

/html/body/div[6]/p[3]

И этого следовало ожидать. Если они должны были генерировать предикаты, как они узнают, какое условие имеет значение? Существуют два круга возможностей.

Ответ 5

Для меня это лучше всего работало (используя элементы org.w3c.dom):

String getXPath(Node node)
{
    Node parent = node.getParentNode();
    if (parent == null)
    {
        return "/";
    }
    return getXPath(parent) + "/" + node.getNodeName();
}

Ответ 6

Что-то вроде этого даст вам простой xpath:

public String getXPath(Node node) {
    return getXPath(node, "");
}

public String getXPath(Node node, String xpath) {
    if (node == null) {
        return "";
    }
    String elementName = "";
    if (node instanceof Element) {
        elementName = ((Element) node).getLocalName();
    }
    Node parent = node.getParentNode();
    if (parent == null) {
        return xpath;
    }
    return getXPath(parent, "/" + elementName + xpath);
}