<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 431,  comments - 344,  trackbacks - 0
    XPath 表達式比繁瑣的文檔對象模型(DOM)導航代碼要容易編寫得多。如果需要從 XML 文檔中提取信息,最快捷、最簡單的辦法就是在 Java™ 程序中嵌入 XPath 表達式。Java 5 推出了 javax.xml.xpath 包,這是一個用于 XPath 文檔查詢的獨立于 XML 對象模型的庫。

    如果要告訴別人買一加侖牛奶,您會怎么說?“請去買一加侖牛奶回來” 還是 “從前門出去,向左轉,走三個街區向右轉,再走半個街區向右轉進入商店。走向四號通道,沿通道走五米向左,拿一瓶一加侖裝的牛奶然后到收銀臺付款。再沿原路回家。” 簡直太可笑了。只要在 “請去買一加侖牛奶回來” 的基礎上稍加指示,多數成人都能自己買回牛奶來。

    查詢語言和計算機搜索與此類似。直接說 “找一個 Cryptonomicon 的副本” 要比編寫搜索某個數據庫的詳細邏輯容易得多。由于搜索操作的邏輯非常相似,可以發明一種通用語言讓您使用 “找到 Neal Stephenson 的所有著作” 這樣的命令,然后編寫對特定數據存儲執行此類查詢的引擎。

    XPath

    在眾多查詢語言之中,結構化查詢語言(SQL)是一種針對查詢特定類型的關系庫而設計和優化的語言。其他不那么常見的查詢語言還有對象查詢語言(OQL)和 XQuery。但本文的主題是 XPath,一種為查詢 XML 文檔而設計的查詢語言。比如,下面這個簡單的 XPath 查詢可以在文檔中找到作者為 Neal Stephenson 的所有圖書的標題:

    //book[author="Neal Stephenson"]/title

    作為對照,查詢同樣信息的純 DOM 搜索代碼如 清單 1 所示:


    清單 1. 找到 Neal Stephenson 所有著作 title 元素的 DOM 代碼
            ArrayList result = new ArrayList();
            NodeList books = doc.getElementsByTagName("book");
            for (int i = 0; i < books.getLength(); i++) {
                Element book = (Element) books.item(i);
                NodeList authors = book.getElementsByTagName("author");
                boolean stephenson = false;
                for (int j = 0; j < authors.getLength(); j++) {
                    Element author = (Element) authors.item(j);
                    NodeList children = author.getChildNodes();
                    StringBuffer sb = new StringBuffer();
                    for (int k = 0; k < children.getLength(); k++) {
                        Node child = children.item(k);
                        // really should to do this recursively
                        if (child.getNodeType() == Node.TEXT_NODE) {
                            sb.append(child.getNodeValue());
                        }
                    }
                    if (sb.toString().equals("Neal Stephenson")) {
                        stephenson = true;
                        break;
                    }
    
                }
    
                if (stephenson) {
                    NodeList titles = book.getElementsByTagName("title");
                    for (int j = 0; j < titles.getLength(); j++) {
                        result.add(titles.item(j));
                    }
                }
    
            }

    不論您是否相信,清單 1 中的 DOM 顯然不如簡單的 XPath 表達式通用或者健壯。您愿意編寫、調試和維護哪一個?我想答案很明顯。

    但是雖然有很強的表達能力,XPath 并不是 Java 語言,事實上 XPath 不是一種完整的編程語言。有很多東西用 XPath 表達不出來,甚至有些查詢也無法表達。比方說,XPath 不能查找國際標準圖書編碼(ISBN)檢驗碼不匹配的所有圖書,或者找出境外帳戶數據庫顯示欠帳的所有作者。幸運的是,可以把 XPath 結合到 Java 程序中,這樣就能發揮兩者的優勢了:Java 做 Java 所擅長的,XPath 做 XPath 所擅長的。

    直到最近,Java 程序執行 XPath 查詢所需要的應用程序編程接口(API)還因形形色色的 XPath 引擎而各不相同。Xalan 有一種 API,Saxon 使用另一種,其他引擎則使用其他的 API。這意味著代碼往往把您限制到一種產品上。理想情況下,最好能夠試驗具有不同性能特點的各種引擎,而不會帶來不適當的麻煩或者重新編寫代碼。

    于是,Java 5 推出了 javax.xml.xpath 包,提供一個引擎和對象模型獨立的 XPath 庫。這個包也可用于 Java 1.3 及以后的版本,但需要單獨安裝 Java API for XML Processing (JAXP) 1.3。Xalan 2.7 和 Saxon 8 以及其他產品包含了這個庫的實現。





    回頁首


    一個簡單的例子

    我將舉例說明如何使用它。然后再討論一些細節問題。假設要查詢一個圖書列表,尋找 Neal Stephenson 的著作。具體來說,這個圖書列表的形式如 清單 2 所示:


    清單 2. 包含圖書信息的 XML 文檔
    <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>
    
        <!-- more books... -->
     
    </inventory>

    抽象工廠

    XPathFactory 是一個抽象工廠。抽象工廠設計模式使得這一種 API 能夠支持不同的對象模型,如 DOM、JDOM 和 XOM。為了選擇不同的模型,需要向 XPathFactory.newInstance() 方法傳遞標識對象模型的統一資源標識符(URI)。比如 http://xom.nu/ 可以選擇 XOM。但實際上,到目前為止 DOM 是該 API 支持的惟一對象模型。

    查找所有圖書的 XPath 查詢非常簡單://book[author="Neal Stephenson"]。為了找出這些圖書的標題,只要增加一步,表達式就變成了 //book[author="Neal Stephenson"]/title。最后,真正需要的是 title 元素的文本節點孩子。這就要求再增加一步,完整的表達式就是 //book[author="Neal Stephenson"]/title/text()

    現在我提供一個簡單的程序,它從 Java 語言中執行這個查詢,然后把找到的所有圖書的標題打印出來。首先,需要將文檔加載到一個 DOM Document 對象中。為了簡化起見,假設該文檔在當前工作目錄的 books.xml 文件中。下面的簡單代碼片段解析文檔并建立對應的 Document 對象:


    清單 3. 用 JAXP 解析文檔
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(true); // never forget this!
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse("books.xml");

    到目前為止,這僅僅是標準的 JAXP 和 DOM,沒有什么新鮮的。

    接下來創建 XPathFactory

    XPathFactory factory = XPathFactory.newInstance();

    然后使用這個工廠創建 XPath 對象:

    XPath xpath = factory.newXPath();

    XPath 對象編譯 XPath 表達式:

    PathExpression expr = xpath.compile("http://book[author='Neal Stephenson']/title/text()");

    直接求值

    如果 XPath 表達式只使用一次,可以跳過編譯步驟直接對 XPath 對象調用 evaluate() 方法。但是,如果同一個表達式要重復使用多次,編譯可能更快一些。

    最后,計算 XPath 表達式得到結果。表達式是針對特定的上下文節點計算的,在這個例子中是整個文檔。還必須指定返回類型。這里要求返回一個節點集:

    Object result = expr.evaluate(doc, XPathConstants.NODESET);

    可以將結果強制轉化成 DOM NodeList,然后遍歷列表得到所有的標題:

            NodeList nodes = (NodeList) result;
            for (int i = 0; i < nodes.getLength(); i++) {
                System.out.println(nodes.item(i).getNodeValue()); 
            }

    清單 4 把上述片段組合到了一個程序中。還要注意,這些方法可能拋出一些檢查異常,這些異常必須在 throws 子句中聲明,但是我在上面把它們掩蓋起來了:


    清單 4. 用固定的 XPath 表達式查詢 XML 文檔的完整程序
    import Java.io.IOException;
    import org.w3c.dom.*;
    import org.xml.sax.SAXException;
    import javax.xml.parsers.*;
    import javax.xml.xpath.*;
    
    public class XPathExample {
    
      public static void main(String[] args) 
       throws ParserConfigurationException, SAXException, 
              IOException, XPathExpressionException {
    
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true); // never forget this!
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse("books.xml");
    
        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XPathExpression expr 
         = xpath.compile("http://book[author='Neal Stephenson']/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()); 
        }
    
      }
    
    }

    XPath 數據模型

    每當混合使用諸如 XPath 和 Java 這樣兩種不同的語言時,必定會有某些將兩者粘合在一起的明顯接縫。并非一切都很合拍。XPath 和 Java 語言沒有同樣的類型系統。XPath 1.0 只有四種基本數據類型:

    • node-set
    • number
    • boolean
    • string

    當然,Java 語言有更多的數據類型,包括用戶定義的對象類型。

    多數 XPath 表達式,特別是位置路徑,都返回節點集。但是還有其他可能。比如,XPath 表達式 count(//book) 返回文檔中的圖書數量。XPath 表達式 count(//book[@author="Neal Stephenson"]) > 10 返回一個布爾值:如果文檔中 Neal Stephenson 的著作超過 10 本則返回 true,否則返回 false。

    evaluate() 方法被聲明為返回 Object。實際返回什么依賴于 XPath 表達式的結果以及要求的類型。一般來說,XPath 的

    • number 映射為 Java.lang.Double
    • string 映射為 Java.lang.String
    • boolean 映射為 Java.lang.Boolean
    • node-set 映射為 org.w3c.dom.NodeList
    XPath 2

    前面一直假設您使用的是 XPath 1.0。XPath 2 大大擴展和修改了類型系統。Java XPath API 支持 XPath 2 所需的主要修改是為返回 XPath 2 新數據類型增加常量。

    Java 中計算 XPath 表達式時,第二個參數指定需要的返回類型。有五種可能,都在 javax.xml.xpath.XPathConstants 類中命名了常量:

    • XPathConstants.NODESET
    • XPathConstants.BOOLEAN
    • XPathConstants.NUMBER
    • XPathConstants.STRING
    • XPathConstants.NODE

    最后一個 XPathConstants.NODE 實際上沒有匹配的 XPath 類型。只有知道 XPath 表達式只返回一個節點或者只需要一個節點時才使用它。如果 XPath 表達式返回了多個節點并且指定了 XPathConstants.NODE,則 evaluate() 按照文檔順序返回第一個節點。如果 XPath 表達式選擇了一個空集并指定了 XPathConstants.NODE,則 evaluate() 返回 null。

    如果不能完成要求的轉換,evaluate() 將拋出 XPathException


    名稱空間上下文

    若 XML 文檔中的元素在名稱空間中,查詢該文檔的 XPath 表達式必須使用相同的名稱空間。XPath 表達式不一定要使用相同的前綴,只需要名稱空間 URI 相同即可。事實上,如果 XML 文檔使用默認名稱空間,那么盡管目標文檔沒有使用前綴,XPath 表達式也必須使用前綴。

    但是,Java 程序不是 XML 文檔,因此不能用一般的名稱空間解析。必須提供一個對象將前綴映射到名稱空間 URI。該對象是 javax.xml.namespace.NamespaceContext 接口的實例。比如,假設圖書文檔放在 http://www.example.com/books 名稱空間中,如 清單 5 所示:


    清單 5. 使用默認名稱空間的 XML 文檔
    <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>
    
        <!-- more books... -->
    
    <inventory>

    查找 Neal Stephenson 全部著作標題的 XPath 表達式就要改為 //pre:book[pre:author="Neal Stephenson"]/pre:title/text()。但是,必須將前綴 pre 映射到 URI http://www.example.com/books。NamespaceContext 接口在 Java 軟件開發工具箱(JDK)或 JAXP 中沒有默認實現似乎有點笨,但確實如此。不過,自己實現也不難。清單 6 對一個名稱空間給出了簡單的實現。還需要映射 xml 前綴。


    清單 6. 綁定一個名稱空間和默認名稱空間的簡單上下文
    import Java.util.Iterator;
    import javax.xml.*;
    import javax.xml.namespace.NamespaceContext;
    
    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();
        }
    
    }

    使用映射存儲綁定和增加 setter 方法實現名稱空間上下文的重用也不難。

    創建 NamespaceContext 對象后,在編譯表達式之前將其安裝到 XPath 對象上。以后就可以像以前一樣是用這些前綴查詢了。比如:


    清單 7. 使用名稱空間的 XPath 查詢
      XPathFactory factory = XPathFactory.newInstance();
      XPath xpath = factory.newXPath();
      xpath.setNamespaceContext(new PersonalNamespaceContext());
      XPathExpression expr 
        = xpath.compile("http://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()); 
      }

    函數求解器

    有時候,在 Java 語言中定義用于 XPath 表達式的擴展函數很有用。這些函數可以執行用純 XPath 很難或者無法執行的任務。不過必須是真正的函數,而不是隨意的方法。就是說不能有副作用。(XPath 函數可以按照任意的順序求值任意多次。)

    通過 Java XPath API 訪問的擴展函數必須實現 javax.xml.xpath.XPathFunction 接口。這個接口只聲明了一個方法 evaluate:

    public Object evaluate(List args) throws XPathFunctionException

    該方法必須返回 Java 語言能夠轉換到 XPath 的五種類型之一:

    • String
    • Double
    • Boolean
    • Nodelist
    • Node

    比如,清單 8 顯示了一個擴展函數,它檢查 ISBN 的校驗和并返回 Boolean。這個校驗和的基本規則是前九位數的每一位乘上它的位置(即第一位數乘上 1,第二位數乘上 2,依次類推)。將這些數加起來然后取除以 11 的余數。如果余數是 10,那么最后一位數就是 X。


    清單 8. 檢查 ISBN 的 XPath 擴展函數
    import Java.util.List;
    import javax.xml.xpath.*;
    import org.w3c.dom.*;
    
    public class ISBNValidator implements XPathFunction {
    
      // This class could easily be implemented as a Singleton.
        
      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;
    
      }
    
    }

    下一步讓這個擴展函數能夠在 Java 程序中使用。為此,需要在編譯表達式之前向 XPath 對象安裝 javax.xml.xpath.XPathFunctionResolver。函數求解器將函數的 XPath 名稱和名稱空間 URI 映射到實現該函數的 Java 類。清單 9 是一個簡單的函數求解器,將擴展函數 valid-isbn 和名稱空間 http://www.example.org/books 映射到 清單 8 中的類。比如,XPath 表達式 //book[not(pre:valid-isbn(isbn))] 可以找到 ISBN 校驗和不匹配的所有圖書。


    清單 9. 識別 valid-isbn 擴展函數的上下文
    iimport javax.xml.namespace.QName;
    import javax.xml.xpath.*;
    
    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;
      }
    
    }

    由于擴展函數必須有名稱空間,所以計算包含擴展函數的表達式時必須使用 NamespaceResolver,即便查詢的文檔沒有使用任何名稱空間。由于 XPathFunctionResolverXPathFunctionNamespaceResolver 都是接口,如果方便的話可以將它們放在所有的類中。


    結束語

    用 SQL 和 XPath 這樣的聲明性語言編寫查詢,要比使用 Java 和 C 這樣的命令式語言容易得多。但是,用 Java 和 C 這樣的圖靈完整語言編寫復雜的邏輯,又比 SQL 和 XPath 這樣的聲明性語言容易得多。所幸的是,通過使用 Java Database Connectivity (JDBC) 和 javax.xml.xpath 之類的 API 可以將兩者結合起來。隨著世界上越來越多的數據轉向 XML,javax.xml.xpath 將與 Java.sql 一樣變得越來越重要。

    posted on 2006-09-18 13:34 周銳 閱讀(923) 評論(0)  編輯  收藏 所屬分類: JavaXMLXSLT
    主站蜘蛛池模板: 亚洲成AV人片在线观看无| 在线亚洲午夜片AV大片| 最近中文字幕大全中文字幕免费| 亚洲成人黄色在线观看| 国产免费观看a大片的网站| 免费av片在线观看网站| 亚洲中文字幕无码爆乳app| 国产亚洲精久久久久久无码AV| 波多野结衣免费在线观看| 免费国产黄网站在线观看动图 | a级片免费在线播放| 亚洲中文字幕无码爆乳| 亚洲欧洲自拍拍偷午夜色无码| 久久久www成人免费毛片| 国产成人无码免费网站| 亚洲欧洲日韩极速播放| 国产亚洲综合久久系列| 国产成人aaa在线视频免费观看| 免费在线看污视频| 国产亚洲精品免费| 亚洲大香伊人蕉在人依线| 亚洲综合色自拍一区| 麻豆国产VA免费精品高清在线 | 成人免费午夜视频| a毛片全部播放免费视频完整18| 亚洲欧美日韩综合久久久| 亚洲成熟xxxxx电影| 亚洲日本一区二区三区在线不卡| 24小时免费直播在线观看| 日韩电影免费在线观看| 成年网站免费入口在线观看| 中文字幕在线日亚洲9| 亚洲精品无码不卡| 亚洲熟妇丰满多毛XXXX| 免费国产成人高清在线观看麻豆| 久久午夜免费视频| 30岁的女人韩剧免费观看| 99久久99这里只有免费的精品| 国产亚洲精彩视频| 亚洲精品9999久久久久无码| 亚洲三级在线播放|