XPath 片段

本文档提供了一些 XPath 代码片段——有关如何实现几个实用函数的示例。这些函数基于公开 XPath 功能供 JavaScript 代码使用的标准接口。这些片段是您可以在实际代码中使用的函数。

特定于节点的求值函数

以下自定义实用函数可用于对给定的 XML 节点评估 XPath 表达式。第一个参数是 DOM 节点或 Document 对象,第二个参数是定义 XPath 表达式的字符串。

示例:定义自定义的特定于节点的 evaluateXPath() 实用函数

js
// Evaluate an XPath expression `expr` against a given DOM node
// or Document object `node`, returning the results as an array
// thanks wanderingstan at morethanwarm dot mail dot com for the
// initial work.
function evaluateXPath(node, expr) {
  const xpe = new XPathEvaluator();
  const nsResolver =
    node.ownerDocument === null
      ? node.documentElement
      : node.ownerDocument.documentElement;
  const result = xpe.evaluate(expr, node, nsResolver, 0, null);
  const found = [];
  let res;
  while ((res = result.iterateNext())) found.push(res);
  return found;
}

请注意,只有当您确定 XPath 表达式中的命名空间前缀与您要查询的文档中的命名空间前缀匹配(并且没有使用默认命名空间)时,才应使用 documentElement。否则,您必须提供自己的 XPathNSResolver 实现。

使用示例

假设我们有以下 XML 文档(另请参阅解析和序列化 XML

示例:用于自定义 evaluateXPath() 实用函数的 XML 文档
xml
<?xml version="1.0"?>
<people>
  <person first-name="eric" middle-initial="H" last-name="jung">
    <address street="321 south st" city="denver" state="co" country="usa"/>
    <address street="123 main st" city="arlington" state="ma" country="usa"/>
  </person>

  <person first-name="jed" last-name="brown">
    <address street="321 north st" city="atlanta" state="ga" country="usa"/>
    <address street="123 west st" city="seattle" state="wa" country="usa"/>
    <address street="321 south avenue" city="denver" state="co" country="usa"/>
  </person>
</people>

您现在可以使用 XPath 表达式“查询”该文档。虽然遍历 DOM 树可以达到类似的结果,但使用 XPath 表达式要快得多,功能也更强大。如果您可以依赖 id 属性,document.getElementById() 仍然很强大,但远不如 XPath 强大。以下是一些示例。

示例:使用自定义 evaluateXPath() 实用函数的 JavaScript 代码
js
// display the last names of all people in the doc
let results = evaluateXPath(people, "//person/@last-name");
for (const i in results)
  console.log(`Person #${i} has the last name ${results[i].value}`);

// get the 2nd person node
results = evaluateXPath(people, "/people/person[2]");

// get all the person nodes that have addresses in denver
results = evaluateXPath(people, "//person[address/@city='denver']");

// get all the addresses that have "south" in the street name
results = evaluateXPath(people, "//address[contains(@street, 'south')]");
console.log(results.length);

docEvaluateArray

以下是一个实用函数,用于将 XPath 结果(有序)放入数组中,无论是否有命名空间解析器等特殊需求。它避免了document.evaluate() 在不需要时使用的复杂语法,也避免了使用XPathResult 上的特殊迭代器(通过返回数组代替)。

示例:定义 docEvaluateArray() 实用函数

js
// Example usage:
// const els = docEvaluateArray('//a');
// console.log(els[0].nodeName); // gives 'A' in HTML document with at least one link

function docEvaluateArray(
  expr,
  context,
  doc = context ? context.ownerDocument : document,
  resolver = null,
) {
  let i;
  const a = [];
  context ||= doc;

  const result = doc.evaluate(
    expr,
    context,
    resolver,
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
    null,
  );
  for (let i = 0; i < result.snapshotLength; i++) {
    a.push(result.snapshotItem(i));
  }
  return a;
}

getXPathForElement

以下函数允许传递一个元素和一个 XML 文档,以查找指向该元素的唯一字符串 XPath 表达式。

示例:定义 getXPathForElement() 实用函数

js
function getXPathForElement(el, xml) {
  let xpath = "";
  let pos, tempItem2;

  while (el !== xml.documentElement) {
    pos = 0;
    tempItem2 = el;
    while (tempItem2) {
      if (tempItem2.nodeType === 1 && tempItem2.nodeName === el.nodeName) {
        // If it is ELEMENT_NODE of the same name
        pos += 1;
      }
      tempItem2 = tempItem2.previousSibling;
    }

    xpath = `*[name()='${el.nodeName}' and namespace-uri()='${
      el.namespaceURI ?? ""
    }'][${pos}]/${xpath}`;

    el = el.parentNode;
  }
  xpath = `/*[name()='${xml.documentElement.nodeName}' and namespace-uri()='${
    el.namespaceURI ?? ""
  }']/${xpath}`;
  xpath = xpath.replace(/\/$/, "");
  return xpath;
}

资源

另见