需要检查XML中是否存在处理指令“<covid19?>”

Need to check if processing instruction `<?covid19?>` is present in XML or not

提问人:Ancy 提问时间:11/4/2022 最后编辑:Sebastian SimonAncy 更新时间:11/5/2022 访问量:90

问:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.1d1 20130915//EN" "JATS-journalpublishing1.dtd"[]>
<article dtd-version="1.1d1" article-type="review-article" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" xml:lang="en">
<front>
<?covid19?>

我需要确定处理指令是否存在于 XML 中。<?covid19?>

jQuery中的伪代码:

$("<?covid19?>").length
javascript html jquery xml processing-instruction

评论

0赞 Sebastian Simon 11/4/2022
jQuery无法找到XML处理指令,所以我不确定你为什么要在这里使用jQuery。您可以使用 TreeWalker API 来查找此处理指令。
0赞 Sebastian Simon 11/5/2022
我已经重新打开了这篇文章。以前的重复目标特定于 Node.js 的 XMLDOM 库。

答:

1赞 Sebastian Simon 11/5/2022 #1

虽然没有 CSS 选择器来选择元素等处理指令,但您可以使用两个不错的 API 来避免手动迭代 DOM 树。

假设您的 XML 文档是 XMLDocument ;您可以通过使用 DOMParser API 解析 XML 字符串来创建一个:theDocument

const xml = `<?xml version="1.0" encoding="UTF-8"?>
<root>
  <?covid19 content-a?>
  <?covid19 content-b?>
  <?thing x?>
  <child>
    <?covid19 content-c?>
    <?thing y?>
  </child>
  <covid19>
    Reject this node.
  </covid19>
</root>`,
  theDocument = new DOMParser().parseFromString(xml, "text/xml");

NodeIterator应用程序接口

使用 NodeIterator API(使用 theDocument.createNodeIterator)可以查找所有处理指令。

const iteratorAll = theDocument
    .createNodeIterator(theDocument, NodeFilter.SHOW_PROCESSING_INSTRUCTION);

const iteratorCOVID19 = theDocument
    .createNodeIterator(theDocument, NodeFilter.SHOW_PROCESSING_INSTRUCTION, {
      acceptNode(node){
        if(node.nodeName.toLowerCase() === "covid19"){
          return NodeFilter.FILTER_ACCEPT;
        }

        return NodeFilter.FILTER_SKIP;
      }
    });

iteratorAll是显示所有处理指令的 a。 是显示所有带有名称的处理指令的 。NodeIteratoriteratorCOVID19NodeIteratorcovid19

TreeWalker API(使用 theDocument.createTreeWalker)与 API 非常相似。NodeIterator

XPath 迭代器 API

也可以使用 XPath(使用 theDocument.evaluate)查找所有处理指令。 结果是 XPathResults

const xPathAll = theDocument
    .evaluate("//processing-instruction()", theDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);

const xPathCOVID19 = theDocument
    .evaluate("//processing-instruction('covid19')", theDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);

XPath 语法说明:

令 牌 意义
// 获取所有后代
processing-instruction() 获取“处理指令”类型的节点
processing-instruction('covid19') 获取类型为“processing instruction”的节点,其名称为covid19

该XPathResult.ORDERED_NODE_ITERATOR_TYPE可用于确保按文档顺序返回节点。

xPathAll是一个迭代器,它显示所有处理指令。 是一个迭代器,它显示所有带有 名称的处理指令。XPathResultxPathCOVID19XPathResultcovid19

迭代帮助程序

这两个 API 在浏览器支持方面非常出色,但这意味着它们已经足够老了,以至于它们没有现代迭代协议。 但这就是发电机被证明有用的地方。

此代码定义了生成器函数,该函数将完全消耗任一迭代器找到的所有节点。 由于 API 获取下一个结果的方法称为 nextNode,而 XPath 方法称为 iterateNext,因此此函数会检查要使用的方法名称中的哪个。 如果找不到适当的方法,它将遵循默认的迭代协议。 然后,一个简单的循环重复调用这些方法之一并产生它们,直到返回为止。consumeDOMIteratorNodeIteratorwhilenull

function* consumeDOMIterator(iterator){
  const method = (iterator instanceof NodeIterator || iterator instanceof TreeWalker
    ? "nextNode"
    : iterator instanceof XPathResult && [
      XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
      XPathResult.ORDERED_NODE_ITERATOR_TYPE
    ].includes(iterator.resultType)
    ? "iterateNext"
    : null);
  
  if(!method){
    yield* iterator[Symbol.iterator]();
    
    return;
  }
  
  let node;
  
  while((node = iterator[method]())){
    yield node;
  }
}

现在,该函数可用于从迭代器创建 Array。Array.from 可以很容易地实现这一点:

Array.from(consumeDOMIterator(iteratorCOVID19))

// Or any of these:
Array.from(consumeDOMIterator(xPathCOVID19))
Array.from(consumeDOMIterator(iteratorAll))
Array.from(consumeDOMIterator(xPathAll))

要检查处理指令是否存在,只需检查 Array 的 or if 或返回 .lengthiteratorCOVID19.nextNode()xPathCOVID19.iterateNext()Node

请注意,它的名称是有原因的:一旦您开始使用此函数遍历 API 结果以创建 Array,结果的状态就会发生变化。 到达末尾后,任一迭代器都将位于文档的“末尾”,因此没有下一个节点。 虽然 API 具有 previousNode,但 XPath Iterator API 没有相应的方法;一般来说,迭代器只能迭代一次。consumeNodeIterator

XPath 快照 API

或者,可用于获得更直接的结果集,这些结果可以更容易地迭代。XPathResult.ORDERED_NODE_SNAPSHOT_TYPE

const snapshotAll = theDocument
    .evaluate("//processing-instruction()", theDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

const snapshotCOVID19 = theDocument
    .evaluate("//processing-instruction('covid19')", theDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

现在,由于 是一个快照,因此可以使用 snapshotLength 来获取所有 snapshotItem。 同样,可用于轻松实现此目的:XPathResultArray.from

Array.from({
  length: snapshotCOVID19.snapshotLength
}, (_, index) => snapshotCOVID19.snapshotItem(index));

与迭代器方法的唯一区别是,当基础文档发生突变时,迭代器不会更改。XPathResult

要检查处理指令是否存在,只需检查 Array 的 or if 返回 .lengthsnapshotCOVID19.snapshotItem(0)Node

完整代码

此代码片段完整演示了如何获取表单的所有处理指令,例如,获取其:<?covid19?>nodeValue

function* consumeDOMIterator(iterator){
  const method = (iterator instanceof NodeIterator || iterator instanceof TreeWalker
    ? "nextNode"
    : iterator instanceof XPathResult && [
      XPathResult.UNORDERED_NODE_ITERATOR_TYPE,
      XPathResult.ORDERED_NODE_ITERATOR_TYPE
    ].includes(iterator.resultType)
    ? "iterateNext"
    : null);
  
  if(!method){
    yield* iterator[Symbol.iterator]();
    
    return;
  }
  
  let node;
  
  while((node = iterator[method]())){
    yield node;
  }
}

const xml = `<?xml version="1.0" encoding="UTF-8"?>
<root>
  <?covid19 content-a?>
  <?covid19 content-b?>
  <?thing x?>
  <child>
    <?covid19 content-c?>
    <?thing y?>
  </child>
  <covid19>
    Reject this node.
  </covid19>
</root>`,
  theDocument = new DOMParser().parseFromString(xml, "text/xml"),
  iteratorAll = theDocument
    .createNodeIterator(theDocument, NodeFilter.SHOW_PROCESSING_INSTRUCTION),
  iteratorCOVID19 = theDocument
    .createNodeIterator(theDocument, NodeFilter.SHOW_PROCESSING_INSTRUCTION, {
      acceptNode(node){
        if(node.nodeName.toLowerCase() === "covid19"){
          return NodeFilter.FILTER_ACCEPT;
        }

        return NodeFilter.FILTER_SKIP;
      }
    }),
  xPathAll = theDocument
    .evaluate("//processing-instruction()", theDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE),
  xPathCOVID19 = theDocument
    .evaluate("//processing-instruction('covid19')", theDocument, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE),
  snapshotAll = theDocument
    .evaluate("//processing-instruction()", theDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE),
  snapshotCOVID19 = theDocument
    .evaluate("//processing-instruction('covid19')", theDocument, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);

const demoIterator = (iterator) => Array.from(consumeDOMIterator(iterator), ({ nodeName, nodeValue }) => [
  nodeName,
  nodeValue
]);
const demoIterator2 = (iterator) => Array.from(consumeDOMIterator(iterator), ({ nodeValue }) => nodeValue);

console.log("All PIs using NodeIterator", demoIterator(iteratorAll));
console.log("All PIs using XPath Iterator", demoIterator(xPathAll));
console.log("All PIs using XPath Snapshot", Array.from({
  length: snapshotAll.snapshotLength
}, (_, index) => {
  const {
      nodeName,
      nodeValue
    } = snapshotAll.snapshotItem(index);
  
  return [
    nodeName,
    nodeValue
  ];
}));

console.log("All node values of <?covid19?> PIs using NodeIterator", demoIterator2(iteratorCOVID19));
console.log("All node values of <?covid19?> PIs using XPath Iterator", demoIterator2(xPathCOVID19));
console.log("All node values of <?covid19?> PIs using XPath Snapshot", Array.from({
  length: snapshotCOVID19.snapshotLength
}, (_, index) => snapshotCOVID19.snapshotItem(index).nodeValue));
.as-console-wrapper { max-height: 100% !important; top: 0; }