Xpath не может запросить тег с пространством имен - PullRequest
4 голосов
/ 09 февраля 2012

У меня есть 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>

Java

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 ]

3 голосов
/ 08 сентября 2012

Гораздо более простой способ справиться с проблемой пространства имен - просто перенаправить вызов из 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;
    }
});
3 голосов
/ 09 февраля 2012

Перед компиляцией вашего xpath вам необходимо зарегистрировать NamespaceContext.

Посмотрите на код в https://github.com/gioele/xpathapi-jaxp/blob/master/src/main/java/it/svario/xpathapi/jaxp/NodeNamespaceContext.java.

Если вы хотите избежать всех этих осложнений, вы можете использовать библиотеку XPathAPI :

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.

...