Xpath无法使用命名空间查询标记

时间:2012-02-09 09:50:45

标签: java xpath xml-namespaces

我有xml如下(Google API),但无法获得gphoto:id元素值。怎么做 ?注意:当我使用domFactory.setNamespaceAware(true);时,/feed/entry xpath停止工作。

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"
      xmlns:gphoto="http://schemas.google.com/photos/2007"
      xmlns:media="http://search.yahoo.com/mrss/"
      xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">
  <entry>
    <title type="text">Test</title>
    <author>
      <name>username</name>
      <uri>https://picasaweb.google.com/113422203255202384532</uri>
    </author>
    <gphoto:id>57060151229174417</gphoto:id>
  </entry>
</feed>

爪哇

NodeList nodes = (NodeList) path(body, "/feed/entry", XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
    Node n = nodes.item(i);

    XPath xpath = XPathFactory.newInstance().newXPath();

    // empty :(
    System.out.println(
       xpath.evaluate("id[namespace-uri()='http://schemas.google.com/photos/2007']",n)
    ); 

    // empty too :(
    System.out.println(
       xpath.evaluate("gphoto:id",n)
    ); 

    // ok
    System.out.println(
       xpath.evaluate("author",n)
    );         
    l.add(new Album("", "", ""));
}

路径方法

private Object path(String content, String path, QName returnType) {
    try {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(new InputSource(new StringReader(content)));
        XPath xpath = XPathFactory.newInstance().newXPath();
        XPathExpression expr = xpath.compile(path);
        return expr.evaluate(doc, returnType);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

已解决根据@gioele回答path()方法现在如下所示:

private Object path(String content, String path, QName returnType) {
    try {
        domFactory.setNamespaceAware(true);
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(new InputSource(new StringReader(content)));
        XPath xpath = XPathFactory.newInstance().newXPath();
        NamespaceContext nsContext = new NamespaceContext() {

            @Override
            public Iterator getPrefixes(String namespaceURI) {
                return null;
            }

            @Override
            public String getPrefix(String namespaceURI) {
                return null;
            }

            @Override
            public String getNamespaceURI(String prefix) {
                if ("gphoto".equals(prefix))
                    return "http://schemas.google.com/photos/2007";
                  if ("media".equals(prefix))
                    return "http://search.yahoo.com/mrss/";
                  if("".equals(prefix))
                      return "http://www.w3.org/2005/Atom"; 
                  throw new IllegalArgumentException(prefix);
            }
        };
        xpath.setNamespaceContext(nsContext);
        XPathExpression expr = xpath.compile(path);
        return expr.evaluate(doc, returnType);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

2 个答案:

答案 0 :(得分:3)

在编译xpath之前,您需要注册NamespaceContext

查看https://github.com/gioele/xpathapi-jaxp/blob/master/src/main/java/it/svario/xpathapi/jaxp/NodeNamespaceContext.java中的代码。

如果您想避免所有这些并发症,可以使用XPathAPI library

Map<String, String> nsMap = new HashMap<String, String>();
nsMap.put(XMLConstants.DEFAULT_NS_PREFIX, "http://www.w3.org/2005/Atom");
nsMap.put("gphoto", "http://schemas.google.com/photos/2007");

List<Node> entries = XPathAPI.selectListOfNodes(doc, "/feed/entry", nsMap);
for (Node entry : entries) {
    String id = XPathAPI.selectSingleNodeAsString(entry, "gphoto:id", nsMap);

    // or, if you prefer a Node
    // Node id = XPathAPI.selectSingleNode(entry, "gphoto:id", nsMap);
}

免责声明:我是XPathAPI-JAXP的创建者。

答案 1 :(得分:3)

处理命名空间问题的一种更简单的方法是将调用从NamespaceContext重定向到文档lookupNamespaceURI()方法。当使用“媒体”等进行调用时,这将返回“http://search.yahoo.com/mrss/”

xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public String getNamespaceURI(String prefix) {
        return doc.lookupNamespaceURI(prefix);
    }
    @Override
    public Iterator<?> getPrefixes(String arg0) {
        return null;
    }
    @Override
    public String getPrefix(String arg0) {
        return null;
    }
});