【问题标题】:Namespace prefix not declared error after extracting a node in OWL/XML file with Java & xPath使用 Java 和 xPath 在 OWL/XML 文件中提取节点后,命名空间前缀未声明错误
【发布时间】:2014-03-04 11:00:06
【问题描述】:

最初我有这个文件。

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <owl:Class />
    <owl:Class />
    <owl:ObjectProperty />
    <Situation:Situation rdf:about"http://localhost/rdf#situa0">
        <Situation:composedBy />
    </Situation:Situation>
</rdf:RDF>

我的目标是使用 xPath "RDF/Situation" 提取节点 Situation 及其内容 ...

<Situation:Situation rdf:about"http://localhost/rdf#situa0">
    <Situation:composedBy />
</Situation:Situation>

我在Java How to extract a complete XML block 中找到了一个很好的例子。

由于我使用命名空间和预定义标签,因此我将标签名称更改为我自己的名称。

这是我的代码

 public static void main(String... args) throws Exception {
        String xml = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"><owl:Class /><owl:Class /><owl:ObjectProperty /><Situation:Situation rdf:about=\"http://localhost/rdf#situa0\" ><Situation:composedBy /></Situation:Situation></rdf:RDF>";
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        Document doc = dbf.newDocumentBuilder().parse(
                new InputSource(new StringReader(xml)));

        XPath xPath = XPathFactory.newInstance().newXPath();
        Node result = (Node) xPath.evaluate("RDF/Situation", doc, XPathConstants.NODE);

        System.out.println(nodeToString(result));
    }

    private static String nodeToString(Node node) throws TransformerException {
        StringWriter buf = new StringWriter();
        Transformer xform = TransformerFactory.newInstance().newTransformer();
        xform.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        xform.transform(new DOMSource(node), new StreamResult(buf));
        return (buf.toString());
    }

我的目标已经实现了 90%,但是我有一个问题,Situation 标签有一个带有前缀 rdf 的属性(如果我删除前缀,并且即使我在根元素中添加了 rdf xmlns,代码也可以工作)

&lt;Situation:Situation rdf:about="http://localhost/rdf#situa0"&gt;

我收到了这个错误

ERROR: 'The namespace prefix' rdf 'has not been declared.' Exception in thread "main" javax.xml.transform.TransformerException: java.lang.RuntimeException: Namespace prefix 'rdf' has not been declared. com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform at (Unknown Source) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform at (Unknown Source)

我像@Ian Roberts 提到的那样添加了dbf.setNamespaceAware(true),所以我在询问owl & Situation 命名空间时遇到了其他错误,在将它添加到根标签后,我在输出中什么也没有,也没有错误。问题是什么 ??问题是变量result,这次是null,所以xPath查询有问题..

我试图在另一个地方查看查询结果,它在an online xPath tester 中运行良好。

那么问题出在哪里??

有没有其他方法可以像这个工作一样????

谢谢 :)

【问题讨论】:

  • 那个 XML 不是命名空间格式良好的——你使用了两个你没有声明的前缀,owl:Situation:——所以你不能使用 XPath 来处理它。
  • 我只需要提取节点“Situation”,我删除了 rdf 前缀,它工作正常。
  • 似乎可以工作,因为您正在使用非命名空间感知解析器解析 XML(您必须在调用 @ 之前在 dbf 上调用 setNamespaceAware(true) 987654336@ 以使其知道命名空间),但这是特定于实现的。 XPath 仅在命名空间格式良好的 XML 上定义,如果您碰巧将另一个 XPath 实现引入您的类路径(例如 Saxon),您的代码可能会停止工作。
  • 如果你配置了NamespaceContext,你可以用xpath来做这件事,但是如果你是从一个DOM文档开始的,那么只使用DOM API并做doc.getElementsByTagNameNS("http://localhost/Situation.owl#", "Situation")会简单得多。或者更好的是,使用适当的 RDF API,例如 Jena,因为将 RDF 视为 XML 是非常脆弱的(有许多不同的方法可以在 XML 中表示相同的 RDF 图)。
  • 请注意,尝试使用 XML 处理工具处理 RDF 通常不是一个好主意,因为同一个 RDF 图可能有许多 XML 序列化。请参阅How to access OWL documents using XPath in Java?this answer 了解更多详情。

标签: java xml xpath owl


【解决方案1】:

有没有其他方法可以做这个工作?

是的,还有其他更合适的方法来完成这项工作。

尝试使用 XML 工具处理 RDF 文档通常不是一个好主意,因为同一个 RDF 图通常可以在 RDF/XML 中以多种不同的方式表示。这在my answerHow to access OWL documents using XPath in Java? 中有更详细的讨论,但我们可以在这里很快看到这个问题。添加一些额外的命名空间声明后,您的数据如下所示:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:Situation="https://stackoverflow.com/q/22170071/1281433/"
    xmlns:owl="http://www.w3.org/2002/07/owl#">
  <owl:Class/>
  <owl:Class/>
  <owl:ObjectProperty/>
  <Situation:Situation rdf:about="http://localhost/rdf#situa0">
    <Situation:composedBy></Situation:composedBy>
  </Situation:Situation>
</rdf:RDF>

同样的RDF图也可以这样序列化:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:Situation="https://stackoverflow.com/q/22170071/1281433/"
    xmlns:owl="http://www.w3.org/2002/07/owl#" > 
  <rdf:Description rdf:nodeID="A0">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
  <rdf:Description rdf:about="http://localhost/rdf#situa0">
    <rdf:type rdf:resource="https://stackoverflow.com/q/22170071/1281433/Situation"/>
    <Situation:composedBy></Situation:composedBy>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A1">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#ObjectProperty"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A2">
    <rdf:type rdf:resource="http://www.w3.org/2002/07/owl#Class"/>
  </rdf:Description>
</rdf:RDF>

如果您正在寻找Situation:Situation 元素,您会在第一个序列化中找到一个,但在第二个序列化中找不到,即使它们是相同 RDF 图。

您或许可以使用 SPARQL 查询来获取您要查找的内容。 describe 查询的典型实现可能会做你想做的事。例如,非常简单的查询

describe <http://localhost/rdf#situa0>

产生这个结果(在 RDF/XML 中):

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:Situation="https://stackoverflow.com/q/22170071/1281433/"
    xmlns:owl="http://www.w3.org/2002/07/owl#">
  <Situation:Situation rdf:about="http://localhost/rdf#situa0">
    <Situation:composedBy></Situation:composedBy>
  </Situation:Situation>
</rdf:RDF>

或者,您可以要求所有类型为Situation:Situation

prefix s: <https://stackoverflow.com/q/22170071/1281433/>
describe ?situation where {
  ?situation a s:Situation .
}
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:s="https://stackoverflow.com/q/22170071/1281433/"
    xmlns:owl="http://www.w3.org/2002/07/owl#">
  <s:Situation rdf:about="http://localhost/rdf#situa0">
    <s:composedBy></s:composedBy>
  </s:Situation>
</rdf:RDF>

这里的重点是针对您拥有的数据类型使用适当的查询语言。您有 RDF,它是一种基于图形的 数据表示。 RDF 图是一组三元组。您的数据是五个三元组:

_:BX2D6970b66dX3A1448f4e1bcfX3AX2D7ffe <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .
<http://localhost/rdf#situa0> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <https://stackoverflow.com/q/22170071/1281433/Situation> .
<http://localhost/rdf#situa0> <https://stackoverflow.com/q/22170071/1281433/composedBy> "" .
_:BX2D6970b66dX3A1448f4e1bcfX3AX2D7ffd <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#ObjectProperty> .
_:BX2D6970b66dX3A1448f4e1bcfX3AX2D7fff <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> .

在Turtle序列化中,图为:

@prefix owl:   <http://www.w3.org/2002/07/owl#> .
@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix Situation: <https://stackoverflow.com/q/22170071/1281433/> .

[ a       owl:Class ] .

<http://localhost/rdf#situa0>
        a                     Situation:Situation ;
        Situation:composedBy  "" .

[ a       owl:Class ] .

[ a       owl:ObjectProperty ] .

您应该使用 SPARQL(标准 RDF 查询语言)或基于 RDF 的 API 从 RDF 文档中提取数据。

【讨论】:

    【解决方案2】:

    您可以通过多种方式解析文件,而无需在 XML 文件中实际包含命名空间。您可以直接将它们添加到您的根节点:

    rootElement.setAttribute("xmlns:owl", "http://www.w3.org/2002/07/owl");
    rootElement.setAttribute("xmlns:Situation", "http://localhost/Situation.owl#");
    

    或者你可以配置一个命名空间解析器:

    xPath.setNamespaceContext(new NamespaceContext() {
        public String getNamespaceURI(String prefix) {
            if (prefix.equals("rdf")) {
                return "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
            } else if (prefix.equals("owl")) {
                return "http://www.w3.org/2002/07/owl";
            } else if (prefix.equals("Situation")) {
                return "http://localhost/Situation.owl#";
            } else {
                return XMLConstants.NULL_NS_URI;
            }
        }
        public String getPrefix(String namespaceURI) { return null;}
        public Iterator getPrefixes(String namespaceURI) { return null;}
    });
    

    您还可以使用独立于命名空间的 XPath 表达式:

    xPath.evaluate("/*[local-name()='RDF']/*[local-name()='Situation']", doc, XPathConstants.NODE);
    

    但您似乎在使用变压器时遇到了错误。它没有找到 rdf 命名空间。这很奇怪。也许它没有被正确地复制到结果节点,因为它是在属性中声明的,并且由于某种原因解析器没有复制它(我只是在猜测)。可能有更好的方法来解决这个问题,但您也可以在将结果节点发送到转换器之前显式地将命名空间前缀添加到结果节点。将其转换为Element,然后使用addAttribute

    Element result = (Element) xPath.evaluate("/RDF/Situation", doc, XPathConstants.NODE);
    result.setAttribute("xmlns:rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
    

    【讨论】:

    • 尝试添加属性(我在上面发布的最后两行)。它至少应该会导致 Transformer 中出现不同的错误。
    猜你喜欢
    • 2019-09-26
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    相关资源
    最近更新 更多