如何使用在Java中具有默认命名空间的XPath检索XML数据?

时间:2014-09-16 20:03:53

标签: java xml xpath javax.xml

我遇到了问题,我已经注意到堆栈溢出,但没有一个解决方案似乎能解决我的问题。

我正在从雅虎检索XML数据,它返回如下(为简洁起见,将其截断)。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<fantasy_content xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng" xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" copyright="Data provided by Yahoo! and STATS, LLC" refresh_rate="31" time="55.814027786255ms" xml:lang="en-US" yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/league/328.l.108462/settings">
    <league>
        <league_key>328.l.108462</league_key>
        <league_id>108462</league_id>
        <draft_status>postdraft</draft_status>
    </league>
</fantasy_content>

我在使用XPath检索任何元素时遇到了问题所以我编写了一个单元测试来尝试解决它,它看起来像:

    final File file = new File("league-settings.xml");
    javax.xml.parsers.DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
    dbFactory.setNamespaceAware(true);
    javax.xml.parsers.DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
    org.w3c.dom.Document doc = dBuilder.parse(file);
    javax.xml.xpath.XPath xPath = XPathFactory.newInstance().newXPath();
    xPath.setNamespaceContext(new YahooNamespaceContext());
    final String expression = "yfs:league";
    final XPathExpression expr = xPath.compile(expression);
    Object nodes = expr.evaluate(doc, XPathConstants.NODESET);

    assert(nodes instanceof NodeList);
    NodeList leagueNodes = (NodeList)nodes;
    int leaguesLength = leagueNodes.getLength();
    assertEquals(leaguesLength, 1);

我为映射命名空间而创建的YahooNamespaceContext类如下所示:

public class YahooNamespaceContext implements NamespaceContext {
    public static final String YAHOO_NS = "http://www.yahooapis.com/v1/base.rng";
    public static final String DEFAULT_NS = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
    public static final String YAHOO_PREFIX = "yahoo";
    public static final String DEFAULT_PREFIX = "yfs";

    private final Map<String, String> namespaceMap = new HashMap<String, String>();
    public YahooNamespaceContext() {
        namespaceMap.put(DEFAULT_PREFIX, DEFAULT_NS);
        namespaceMap.put(YAHOO_PREFIX, YAHOO_NS);
    }

    public String getNamespaceURI(String prefix) {
        return namespaceMap.get(prefix);
    }

    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }

    public Iterator<String> getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }

}

对于具有更多XML命名空间经验或对Xpath编译/评估的调试技巧的人的任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:2)

如果问题是您将结果节点列表的长度设为零,您是否尝试过更改

final String expression = "yfs:league";

final String expression = "//yfs:league";

评估XPath表达式doc的上下文似乎是文档的根节点。 dBuilder.parse(file)返回文档 root 节点,而不是最外层元素(a.k.a. 文档元素)。请记住,在XPath中,root node is not an element。所以doc 不是yfs:fantasy_content元素节点,而是它(不可见)父节点。

在该上下文中,XPath表达式"yfs:league"将仅选择作为该根节点的直接子节点的元素,其中没有yfs:league - 仅yfs:fantasy_content。< / p>

答案 1 :(得分:1)

XPath表达式yfs:league等同于child::yfs:league。这意味着:使用指定的本地名称(doc)和名称空间URI(league)查找http://fantasysports.yahooapis.com/fantasy/v2/base.rng的直接子节点(而不是后代)。

您必须考虑最外层的元素(fantasy_content)或搜索后代而不是子节点。

更换

final String expression = "yfs:league";

final String expression = "yfs:fantasy_content/yfs:league";

final String expression = "//yfs:league";

将解决问题。