如何将org.w3c.dom.NodeList与Java 8 Stream API结合使用



我认为Interface org.w3c.dom.NodeList缺少一个stream()函数来利用Java 8的Stream API的优势。考虑到引入了默认方法以确保向后兼容性,我不明白为什么这个接口没有stream()功能。

所以我的问题是:

  • 如何将NodeList与Stream API结合使用
  • 如果不鼓励这样做,原因是什么

提前感谢!

编辑:我目前正在使用这个实用程序包装:

private static Stream<Node> nodeStream(NodeList list) {
    List<Node> nodes = new ArrayList<>();
    for (int n = 0; n < list.getLength(); ++n) {
        nodes.add(list.item(n));
    }
    return nodes.stream();
}

DOM是一个奇怪的野兽,API是由W3C以依赖于语言的方式定义的,然后映射到各种不同的编程语言中,因此Java不能向最初不属于DOM规范的核心DOM接口添加任何特定于Java的内容。

因此,虽然您不能将NodeList用作流,但您可以轻松地从NodeList创建流,例如使用

Stream<Node> nodeStream = IntStream.range(0, nodeList.getLength())
                                   .mapToObj(nodeList::item);

然而,有一个很大的警告-DOM NodeListlive,它反映了自创建列表以来对原始DOM树的更改。如果在DOM树中添加或删除元素,它们可能会神奇地从现有的NodeList中出现或消失,如果在迭代过程中发生这种情况,可能会产生奇怪的效果。如果你想要一个"死"节点列表,你需要将它复制到一个数组或列表中,就像你已经在做的那样。

考虑到引入了默认方法以确保向后兼容性,我不明白为什么这个接口没有stream((函数。

该接口是在Java 8存在之前定义的。由于Stream在Java 8之前并不存在,因此NodeList无法支持它

如何将NodeList与Stream API结合使用?

你不能。至少,不是直接的。

如果不鼓励这样做,原因是什么?

它并没有"气馁"。相反,它不受支持。

主要原因是历史。请参见上文。

负责为Java指定org.w3c.dom API的人员(即W3联盟(可能会推出对Java 8更友好的新版API。然而,这将引入一系列新的兼容性问题。新版本的API将与当前的API不兼容,并且与Java 8之前的JVM也不兼容。


然而,这比让W3 Consortium更新API要复杂得多。

DOM API是在CORBA IDL中定义的,Java API是通过将CORBA Java映射应用于IDL而"生成"的。此映射由OMG指定。。。而不是W3联盟。因此,创建org.w3c.domAPI的"Java8Stream友好型"版本将需要OMG将CORBAJava映射更新为Stream感知(至少从CORBA兼容性的角度来看,这将是有问题的(,或者中断Javaneneneba API和CORBA之间的连接。

不幸的是,在刷新IDL到Java的映射时,很难发现OMG世界中发生了什么(如果有的话(。。。除非你为OMG成员组织工作,等等。我没有。

下面是一个流用于查找特定NodeList元素的示例:

private String elementOfInterest;       // id of element
private String elementOfInterestValue;  // value of element
public boolean searchForElementOfInterest(Document doc)
{
        boolean bFound=false;
        NodeList nList = doc.getElementsByTagName("entity");
        // since NodeList does not have stream implemented, then use this hack
        Stream<Node> nodeStream = IntStream.range(0, nList.getLength()).mapToObj(nList::item);
        // search for element of interest in the NodeList
        if(nodeStream.parallel().filter(this::isElementOfInterest).collect(Collectors.toList()).size() > 0)
                bFound=true;
        return bFound;
}
private boolean isElementOfInterest(Node nNode)
{
        boolean bFound=false;
        assert(nNode != null);
        if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                Element eElement = (Element) nNode;
                String id = eElement.getElementsByTagName("id").item(0).getTextContent();
                String data = eElement.getElementsByTagName("data").item(0).getTextContent();
                if (id.contentEquals(elementOfInterest) && data.contentEquals(elementOfInterestValue))
                        bFound = true;
        }
        return bFound;
}

java8 Stream.iterate
这样使用:

    Stream.iterate(0, i -> i + 1)
          .limit (nodeList.getLength())
          .map (nodeList::item).forEach...

对于java9iterate,有一个iterate的增强版本,而以前的版本也可用:

    Stream.iterate(0, i -> i < nodeList.getLength(), i -> i + 1)
          .map (nodeList::item).forEach...

迭代的两个版本在Java14 中仍然相同

如果您想将子节点转换为节点流,则变化较小。仅元素:

private Stream<Element> nodeLisToStreamElements(NodeList nodeList) {
    return IntStream.range(0, nodeList.getLength())
        .mapToObj(nodeList::item)
        .filter(node -> node instanceof Element)
        .map(node -> (Element) node);
}

最新更新