Java XPath API语法教程

xpath 是XML Path的简称, 它比繁琐的文档对象模型(DOM)导航代码要容易编写得多。如果需要从 XML 文档中提取信息,最快捷、最简单的办法就是在 Java 程序中嵌入 XPath 表达式。Java 5推出了javax.xml.xpath包,这是一个用于XPath文档查询的独立于XML对象模型的库。

XPath常用语法对照表

常用的XPATH

表达式

说明

//

表示返回XML文档中的所有符合查找准则的元素,而忽略文档中元素的位置级别

/

表示绝对路径

News/Links/name

表示相对路径

./

表示当前节点

../

表示父节点

*

表所所有元素

or

And

其它表达式

=,!=,<,>,>=,<=

Text()

文本

Node()

节点

number last()

last 函数返回一个数字,该数字等于从表达式求值上下文中的上下文大小(即返回节点个数)

number position()

position函数返回一个数字,该数字等于从表达式求值上下文中的上下文位置(即当前位置)

number count(node-set)

count 函数返回在参数node-set中节点的个数。

node-set id(object)

 

string string(object?)

string 函数将对象依下列各项转换成字符

string concat(stringstringstring*)

concat 函数返回它的参数的联结。

boolean starts-with(stringstring)

如果第一个字符串参数以第二个字符串参数起头,starts-with 函数返回真,否则,返回假。

boolean contains(stringstring)

如果第一个字符串参数包含第二个字符串参数,contains 函数返回真,否则,返回假。

string substring-before(string,string)

substring-before 函数返回第一个字符串参数在第二个参数首次出现之前的子字符串,或者,如果第一个字符串参数不包含第二个字符串参数则返回空字符串。例如,substring-before("1999/04/01","/") 返回 1999。

string substring-after(string,string)

substring-after 函数返回第一个字符串参数在第二个参数首次出现之后的子字符串,或者,如果第一个字符串参数不包含第二个字符串参数则返回空字符串。例如, substring-after("1999/04/01","/") 返回 04/01,而 substring-after("1999/04/01","19") 返回 99/04/01。

string substringstring , number ,number? )

substring 函数返回第一个字符串参数从第二个参数所指定的位置开始,以第三个参数为长度的子字符串。例如, substring("12345",2,3) 返回"234"。如果没有第三个参数,则返回从第二个参数所指定的位置开始直到结束。例如, substring("12345",2) 返回"2345"。

number string-lengthstring? )

string-length 返回字符串中字符的个数。如果参数省略,则缺省为一个上下文节点再转换成字符串,也就是上下文节点的 字符串值。

string normalize-space(string?)

normalize-space 函数返回空白符规范化 后的参数字符串,该规范化是清除领头及结尾的空白字以及用一个空白符替换连续的空白符。空白符字符与XML 中的产生式 S 所允许的是一样的。如果参数省略,则缺省为一个上下文节点再转换成字符串,也就是上下文节点的 字符串值。

string translate(stringstring,string)

translate 函数返回第一个参数的字符串,其中有在第二个参数中出现的字符都被在第三个参数中相对应位置的字符所替换。例如,translate("bar","abc","ABC") 返回字符串 BAr。如果,第二个参数里的字符在第三个参数的相对应位置没有字符(因为第二个参数里的字符串比第三个参数的字符串长),那么,第一个参数里的那个字符将被移去。例如,translate("--aaa--","abc-","ABC") 返回 "AAA"。如果在第二个参数里的字符出现超过一次,那么,第一次的出现决定替换的字符。如果第三个参数里的字符串比第二个参数的字符串长, 那么,多余的字符将被忽略。

boolean not(boolean)

如果参数为真 not 函数返回真,否则返回假。

boolean true()

true 函数返回真。

boolean false()

The false 函数返回假。

number number(object?)

number 函数参数依下列各项转换成数字

number sum(node-set)

对于在参数节点集合的每个节点,sum 函数返回节点字符串值转换成数字后的和。

number floor(number)

floor 函数返回不大于参数的整数的最大数 (最接近于正无穷大)

number ceiling(number)

ceiling 函数返回不小于参数的整数的最小数 (最接近于负无穷大

number round(number)

round 函数返回最接近于参数的整数。

//*

获得所有节点

../*

获得当前节点的父节点的所有节点

//Links

获得所有的Links节点

//Links[name=”网易”]

获得子节点name的文本为“网易”的所有Links节点

//Links[@id=”1”]

获得属性ID=1的所有Links节点

//*[name=”新浪”]

获得子节点name的文本为“新浪”的所有节点

//*[@id=”1”]

获得属性ID=1的所有节点

//*[@id]

获得存在属生ID的所有节点点

//*[name]

获得存在子节点name的所有节点

//Links[序号]

获得返回的N个Links节点中的第序号个节点

//Links[1 or 2]

获得返回的N个Links节点的中第一个和第二个节点

//*[name=”网易” and @id=”1”]

获得所有子点节name的文本为“网易” 且自己的属生id=“1”的节点

//text()

选择所有的正文节点(即叶子节点)

//Links[position()=last()]

获得返回的N个Links节点中的最后一个节点

等同于//Links[last()]

//*[contains(name,”新”)]

获得子节点name的文本中包含“新”的所有节点

//Links[1]

获得返回的N个Links节点中的第一个节点

//Links[1]/name[1]

获得第一个Links的第一个name节点

//Links//name

获得所有Links节点下的所有name节点

//*[@id>1]

获得属性ID>1的所有节点

//*[number(@id)+1>1]

获得属生ID的值加1>1的所有节点

//*[number(text())>1]

获得文本节点的文本值大于1的所有节点

//*[(number(text()) mode 2)=1]

获得文本节点的文本值为基数的所有节点

XPath例子

1. 查询一个图书列表,寻找 Neal Stephenson 的著作。

<inventory>
    <book year="2000">
        <title>Snow Crash</title>
        <author>Neal Stephenson</author>
        <publisher>Spectra</publisher>
        <isbn>0553380958</isbn>
        <price>14.95</price>
    </book>
    <book year="2005">
        <title>Burning Tower</title>
        <author>Larry Niven</author>
        <author>Jerry Pournelle</author>
        <publisher>Pocket</publisher>
        <isbn>0743416910</isbn>
        <price>5.99</price>
    <book>
    <book year="1995">
        <title>Zodiac</title>
        <author>Neal Stephenson<author>
        <publisher>Spectra</publisher>
        <isbn>0553573862</isbn>
        <price>7.50</price>
    <book>
</inventory>

寻找 Neal Stephenson 的著作的XPath语法://book[author="Neal Stephenson"]。为了找出这些图书的标题,只要增加一步,表达式就变成了 //book[author="Neal Stephenson"]/title。最后,真正需要的是 title 元素的文本节点孩子。这就要求再增加一步,完整的表达式就是 //book[author="Neal Stephenson"]/title/text()。

2. 使用默认名称空间的XML文档查找Neal Stephenson全部著作

<inventory xmlns="http://www.example.com/books">
    <book year="2000">
        <title>Snow Crash</title>
        <author>Neal Stephenson</author>
        <publisher>Spectra</publisher>
        <isbn>0553380958</isbn>
        <price>14.95<price>
    </book>
<inventory>

查找 Neal Stephenson 全部著作标题的 XPath 表达式就为 //pre:book[pre:author="Neal Stephenson"]/pre:title/text()。但是,必须将前缀 pre 映射到 URI http://www.example.com/books。 NamespaceContext接口在 Java 软件开发工具箱(JDK)或 JAXP 中没有默认实现似乎有点笨,但确实如此。

Java代码如下:

//绑定一个名称空间和默认名称空间的简单上下文
public class PersonalNamespaceContext implements NamespaceContext {
    public String getNamespaceURI(String prefix) {
        if (prefix == null) throw new NullPointerException("Null prefix");
        else if ("pre".equals(prefix)) return "http://www.example.org/books";
        else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
        return XMLConstants.NULL_NS_URI;
    }

    // This method isn't necessary for XPath processing.
    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }

    // This method isn't necessary for XPath processing either.
    public Iterator getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }
}
//使用名称空间的 XPath 查询
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new PersonalNamespaceContext());
XPathExpression expr = xpath.compile("//pre:book[pre:author='Neal Stephenson']/pre:title/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
    System.out.println(nodes.item(i).getNodeValue());
}

3. XPath扩展函数检查ISBN

ISBNValidator 函数求解器将扩展函数valid-isbn和名称空间 http://www.example.org/books 映射起来。比如,XPath 表达式 //book[not(pre:valid-isbn(isbn))] 可以找到ISBN校验和不匹配的所有图书。

public class ISBNValidator implements XPathFunction {
    public Object evaluate(List args) throws XPathFunctionException {
        if (args.size() != 1) {
            throw new XPathFunctionException("Wrong number of arguments to valid-isbn()");
        }

        String isbn;
        Object o = args.get(0);
        // perform conversions
        if (o instanceof String) isbn = (String) args.get(0);
        else if (o instanceof Boolean) isbn = o.toString();
        else if (o instanceof Double) isbn = o.toString();
        else if (o instanceof NodeList) {
            NodeList list = (NodeList) o;
            Node node = list.item(0);
            // getTextContent is available in Java 5 and DOM 3.
            // In Java 1.4 and DOM 2, you'd need to recursively 
            // accumulate the content.
            isbn = node.getTextContent();
        } else {
            throw new XPathFunctionException("Could not convert argument type");
        }

        char[] data = isbn.toCharArray();
        if (data.length != 10) return Boolean.FALSE;
        int checksum = 0;
        for (int i = 0; i < 9; i++) {
            checksum += (i + 1) * (data[i] - '0');
        }
        int checkdigit = checksum % 11;
        if (checkdigit + '0' == data[9] || (data[9] == 'X' && checkdigit == 10)) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
}
//识别 valid-isbn 扩展函数的上下文
public class ISBNFunctionContext implements XPathFunctionResolver {
    private static final QName name = new QName("http://www.example.org/books", "valid-isbn");
    public XPathFunction resolveFunction(QName name, int arity) {
        if (name.equals(ISBNFunctionContext.name) && arity == 1) {
            return new ISBNValidator();
        }
        return null;
    }
}