【问题标题】:How to use name() and/or node() in Java XPath query?如何在 Java XPath 查询中使用 name() 和/或 node()?
【发布时间】:2014-08-29 14:53:11
【问题描述】:

我有一些大致如下所示的 XML:

<project type="mankind">
    <suggestion>Build the enterprise</suggestion>
    <suggestion>Learn Esperanto</suggestion>
    <problem>Solve world hunger</suggestion>
    <discussion>Do Vulcans exist</discussion>
</project>

我想使用 XPath 找出使用 Java 的二级元素的名称(可能有一些我事先不知道的元素)。这是我试过的代码:

public NodeList xpath2NodeList(Document doc, String xPathString) throws XPathExpressionException {
     XPath xpath = XPathFactory.newInstance().newXPath();
     MagicNamespaceContext nsc = new MagicNamespaceContext();
     xpath.setNamespaceContext(nsc);
     Object exprResult = xpath.evaluate(xPathString, doc, XPathConstants.NODESET);
     return (NodeList) exprResult;
}

我的 XPath 是 /project/*/name()。我得到了错误:

javax.xml.transform.TransformerException:未知节点类型:名称

/project/suggestion 之类的查询按预期工作。我错过了什么?我想得到一个包含标签名称的列表。

【问题讨论】:

  • 您使用的是哪个版本的 XPath?
  • 你应该使用 /project/@name 而不是 /project/*/name()
  • @aoulhent OP 正在尝试检索/project/*元素名称,而不是该属性的内容。
  • @MathiasMüller Java6(不要问)。并感谢澄清。是的,我不需要顶层的属性,而是元素。我会更新问题以消除潜在的混淆

标签: java xml xpath


【解决方案1】:

Java6(别问)。

我认为您的实现只支持 XPath 1.0。如果这是真的,那么只有以下方法可行:

"name(/project/*)"

原因是在 XPath 1.0 模型中,您不能将函数(如 name())用作路径表达式中的一个步骤。您的代码抛出异常,在这种情况下,处理器将您的函数name() 误认为是未知的节点测试(如comment())。但是使用路径表达式作为name()函数的参数并没有错。

不幸的是,如果一个只能处理单个节点作为参数的 XPath 1.0 函数被赋予一个节点序列,那么只会使用第一个节点。因此,您很可能只会得到第一个元素名称。

XPath 1.0 的操作能力非常有限,解决此类问题的最简单方法通常是使用使用 XPath 作为查询语言的高级语言(在您的情况下为 Java)。或者换一种说法:用 Java 编写一个 XPath 表达式来检索所有相关节点并遍历结果,返回元素名称。

使用 XPath 2.0,您的初始查询会很好。另见this related question

【讨论】:

  • 好多了,现在我得到一个 org.apache.xpath.XPathException: Can not convert #STRING to a NodeList!,但我猜这是由于我的 XPathConstants.NODESET。当我使用 XPathConstants.STRING 尝试它时,它可以工作。现在....有没有办法确定它是仅是字符串还是返回的节点集。如果我省略参数,它总是返回一个字符串
  • @stwissel 我不熟悉Java Transformer,但是评估路径表达式的方法是否有必要返回NodeList?你能写出不同的方法吗,一种用于查找节点,另一种用于字符串?
  • 那是 B 计划。当然,我必须猜测哪个 XPath 表达式(我从配置文件中读取它们)将返回一个字符串,哪个返回一个节点集(或者我只是尝试捕获错误)。
  • @stwissel 正如我所说,尽管我很想在 Java 方面提供帮助,但这并不完全是我的专业领域。我发现必须提前确定返回类型很麻烦,我相信有更好的方法。如果只有一种方法来评估 XPath,只编写评估为节点集的表达式,返回节点并将它们传递给另一个方法,该方法将节点集作为参数并返回它们的名称?
  • 我不会写 XPath,所以我无法控制。我预计 90% 是节点集,所以我将在它周围放置一个错误处理程序,如果它失败,请使用文本重试。不性感,但 XML 不是很大,所以我们谈论毫秒延迟。 Thx 用于具有语法的指针。感谢快速回复
【解决方案2】:

下面的代码可能会回答你原来的问题。

    package com.example.xpath;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;

    import javax.xml.parsers.DocumentBuilder;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.xpath.XPath;
    import javax.xml.xpath.XPathConstants;
    import javax.xml.xpath.XPathExpression;
    import javax.xml.xpath.XPathExpressionException;
    import javax.xml.xpath.XPathFactory;

    import org.w3c.dom.Document;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    import org.xml.sax.SAXException;

    public class XPathReader {

        static XPath xPath =  XPathFactory.newInstance().newXPath();

        public static void main(String[] args) {

            try {
                FileInputStream file = new FileInputStream(new File("c:/mankind.xml"));

                DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

                DocumentBuilder builder =  builderFactory.newDocumentBuilder();

                Document xmlDocument = builder.parse(file);

                XPathExpression expr = xPath.compile("//project/*");
                NodeList list= (NodeList) expr.evaluate(xmlDocument, XPathConstants.NODESET);
                for (int i = 0; i < list.getLength(); i++) {
                    Node node = list.item(i);
                    System.out.println(node.getNodeName() + "=" + node.getTextContent());
                }

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (XPathExpressionException e) {
                e.printStackTrace();
            }        
        }
    }

假设输入正确(解决世界饥饿),代码应该打印:

suggestion=建立企业
建议=学习世界语
问题=解决世界饥饿
讨论=火神存在吗

【讨论】:

  • 感谢代码。这里的挑战:XPath 返回整个元素,而不仅仅是名称。我需要能够区分基于 XPath 获取元素的名称或内容。使用返回的 Nodeset 后处理步骤需要这样做,但除了 XPath 表达式之外还需要额外的“知识”
  • 在 println 上的代码显示了如何只检索名称;即 node.getNodeName().
  • 抱歉不清楚。我感谢您的帮助。 XPath 表达式将从外部源中提取。当我阅读“/project/*”(一个斜杠就足够了,因为它位于顶层)时,我得到了一个节点设置。当我取回它时,我不知道 XPath 是用于检索节点名称还是节点值。所以 XPath 将是模棱两可的。在您的示例中很好地显示了使用 Java 类检索名称,但我正在寻找直接返回它们的 XPath。感谢您在这里提供帮助
  • 我不明白 NodeList 如何只能根据 XPath 表达式发回名称或值。当 xPathString = "/project/*" 或 xPathString = "name(/project/*[3])" 时它需要返回一个 NodeList 或 String。一种选择是方法签名为“public List xpath2NodeList(Document doc, String xPathString) throws XPathExpressionException”。将 XPathConstants 更改为 NODESET 或 STRING,具体取决于 xPathString 是否以 'name(' 开头。使用 NODESET 时,将所有检索到的值放在 List 中。使用 String 时,仅返回 List 索引 0 中的元素名称。
  • 这正是我的问题。最好的解决方案是使用 /name() 工作的更高版本的 xpath 引擎。如果某物是节点集,则 name(somthing) 返回连接的字符串。因此,正如您指出的那样,需要一些错误处理
猜你喜欢
  • 2014-12-07
  • 1970-01-01
  • 1970-01-01
  • 2011-03-22
  • 2011-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
相关资源
最近更新 更多