2012年5月15日 星期二

利用XPath輕鬆使用XML - XmlDocument, XmlNode, XML

因為前陣子有朋友問我要怎麼讀XML檔案到自己的電腦,那時候腦袋馬上就浮現 DOM 物件跟利用 XPath 的字眼,隨口脫出,但是卻換來對方滿腦的問號,所以趁午休時間整理一下。

DOM ( Document Object Model ) 全名為文件物件模型。 這定義 威基 (Wiki) 裡面寫得很不錯。簡單地說,你可以想像一份 HTML 或 XML 等相關文件都是一種 DOM 物件,而文件裡的每個標籤(tag) 都是該物件中的一個節點 (node) 或是稱作元素 (element)。先前是利用 JavaScript 使用為主要大宗。因為最早期的瀏覽器大戰,就是以微軟以及 Netscape為爭取使用率,便積極開發各自的 JavaScript 因而促使 DOM 物件使用的成熟。

之後個語法也都有相關的 DOM 物件相關的使用 API ,除一開始的 JavaScript 外,PHP、 ASP.net 也都有陸續支援的 API 套件。

XPath (XML Path Language) 便是XML路徑語言,是一種用來確定XML文檔中某部分位置的語言。



以下介紹如何利用ASP.net 的 C# 語法來使用 DOM 相關物件。需引用 System.Xml 類別參考來進行。其內容皆有符合W3C明確的規範。

  • XML 1.0 - http://www.w3.org/TR/1998/REC-xml-19980210 - 包含 DTD 支援。
  • XML 命名空間 - http://www.w3.org/TR/REC-xml-names/ - 資料流層級和 DOM。
  • XSD 結構描述 - http://www.w3.org/2001/XMLSchema
  • XPath 運算式 - http://www.w3.org/TR/xpath
  • XSLT 轉換 - http://www.w3.org/TR/xslt
  • DOM 層級 1 核心 - http://www.w3.org/TR/REC-DOM-Level-1/
  • DOM 層級 2 核心 - http://www.w3.org/TR/DOM-Level-2/

主要類別為 XmlDocument、XmlNode。其使用範例如下。

1.首先需要來源資料(XML),我們以雅虎新聞娛樂的 Rss 為範本。其範本類似以下文件。
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" xmlns:ynews="http://news.yahoo.com/rss/" version="2.0">
    <channel>
        <title>影劇新聞 - 頭條新聞 - Yahoo!奇摩新聞</title>
        <link>http://tw.news.yahoo.com/entertainment/</link>
        <description>瀏覽 Yahoo!奇摩新聞上的最新影劇頭條新聞。尋找最新新聞報導,包括影劇]頭條新聞的相關分析與意見。</description>
        <language>zh-Hant-TW</language>
        <copyright>Copyright (c) 2012 Yahoo! Inc. All rights reserved</copyright>
        <pubDate>2012-05-15T05:30:00+08:00</pubDate>
        <ttl>5</ttl>
        <image>
            <title>影劇新聞 - 頭條新聞 - Yahoo!奇摩新聞</title>
            <link>http://tw.news.yahoo.com/entertainment/</link>
            <url>http://l.yimg.com/os/mit/media/m/index/img/Yahoo_logo_zh-Hant-TW.gif</url>
        </image>
        <item>
            <title>Makiyo信教悔過 媽幸福知足</title>
            <description>內文描述</description>
            <link>http://tw.news.yahoo.com/makiyo%E4%BF%A1%E6%95%99%E6%82%94%E9%81%8E-ma%E5%AA%BD%E5%B9%B8%E7%A6%8F%E7%9F%A5%E8%B6%B3-213000158.html</link>
            <pubDate>2012-05-14T21:30:00Z</pubDate>
            <source>中時電子報</source>
            <guid isPermaLink="false">makiyo%E4%BF%A1%E6%95%99%E6%82%94%E9%81%8E-ma%E5%AA%BD%E5%B9%B8%E7%A6%8F%E7%9F%A5%E8%B6%B3-213000158</guid>
            <media:content url="http://l3.yimg.com/bt/api/res/1.2/Z5l_sPpql9lcB7OVwlsThA--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3B4b2ZmPTUwO3B5b2ZmPTA7cT04NTt3PTEzMA--/http://media.zenfs.com/zh_hant_tw/News/Chinatimes/C012161O.jpg" type="image/jpeg" width="130" height="86"></media:content>
            <media:text type="html">新聞內文</media:text>
            <media:credit role="publishing company"></media:credit>
        </item>
        <item>
            <title>姚悠哉逛夜市 網友轟沒擔當</title>
            <description>內文描述</description>
            <link>http://tw.news.yahoo.com/%E5%A7%9A%E5%85%83%E6%B5%A9%E6%9D%AD%E5%B7%9E%E6%82%A0%E5%93%89%E9%80%9B%E5%A4%9C%E5%B8%82-%E9%9A%8B%E6%A3%A0%E9%99%AA%E5%AA%BD%E8%BF%94%E5%8F%B0%E9%A1%AF%E7%96%B2%E6%85%8B-221110032.html</link>
            <pubDate>2012-05-14T22:11:10Z</pubDate>
            <source>NOWnews</source>
            <guid isPermaLink="false">%E5%A7%9A%E5%85%83%E6%B5%A9%E6%9D%AD%E5%B7%9E%E6%82%A0%E5%93%89%E9%80%9B%E5%A4%9C%E5%B8%82-%E9%9A%8B%E6%A3%A0%E9%99%AA%E5%AA%BD%E8%BF%94%E5%8F%B0%E9%A1%AF%E7%96%B2%E6%85%8B-221110032</guid>
            <media:content url="http://l2.yimg.com/bt/api/res/1.2/55cGpXkc7VS1kW1cqpQliQ--/YXBwaWQ9eW5ld3M7Zmk9ZmlsbDtoPTg2O3E9ODU7dz0xMzA-/http://media.zenfs.com/zh_hant_tw/News/Chinatimes/C0121478.jpg" type="image/jpeg" width="130" height="86"></media:content>
            <media:text type="html">新聞內文</media:text>
            <media:credit role="publishing company"></media:credit>
        </item>
    </channel>
</rss>


2.定義一頁 Default.aspx,放一標準 >> BulletedList 控制項,屬性 DisplayMode 設定為 "HyperLink",並雙擊 Default.aspx.cs 撰寫 pageLoad 讀取的Code
protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            XmlDocument objDom = new XmlDocument();
            objDom.Load(@"http://tw.news.yahoo.com/rss/entertainment");
//使用到XPath讀取資料
            XmlNode ndRoot = objDom.SelectSingleNode(@"/rss");
//使用到XPath讀取node集合
            XmlNodeList ndsSelect = ndRoot.SelectNodes(@"/rss/channel/item");

            if (ndsSelect!=null)
            {
                foreach (XmlNode ndItem in ndsSelect)
                {
//使用到node的ChildNode來找尋子項目
                     this.BulletedList1.Items.Add(new ListItem(ndItem.ChildNodes[0].InnerXml, ndItem.ChildNodes[2].InnerXml));
                }  
            }
            
        }
    }


3.XPath 的語法大概有這些,整理如下

XPath is an emerging standard for directly accessing one or more nodes in an XML document using a simple filename-like syntax. For example, the XPath "/*/author" would select all of the grandchildren named "author", starting the search at the top-level document. Electric XML includes a useful subset of XPath functionality, and supports the following special tokens:
name
matches all nodes on the current level with the specified name
[n]
matches the nth node on the current level, with n=1 denoting the first node
name[n]
matches the nth element on the current level with the specified name
*
matches all nodes on the current level
/
if used as the first character, denotes the top-level document, otherwise denotes moving down a level
..
go up one level
.
the current level
//
the current level and all sublevels to any depth
[@key='value']
all elements with an attribute that matches the specified key/value pair
name[@key='value']
all elements with the specified name and an attribute that matches the specified key/value pair
[text()='value']
all elements with the specified text
name[text()='value']
all elements with the specified name and text
@name
the attribute with the specified name
@*
all attributes

XPath範例XML ( 轉自XPath語法介紹 )
<?xml version="1.0" encoding="iso-8859-1"?>
<catalog>
    <cd country="USA">
        <title>Empire Burlesque</title>
        <artist>Bob Dylan</artist>
        <price>10.90</price>
    </cd>
    <cd country="UK">
        <title>Hide your heart</title>
        <artist>Bonnie Tyler</artist>
        <price>9.90</price>
    </cd>
    <cd country="USA">
        <title>Greatest Hits</title>
        <artist>Dolly Parton</artist>
        <price>9.90</price>
    </cd>
</catalog>


定位節點
XML是樹狀結構,類似檔案系統內資料夾的結構,XPath也類似檔案系統的路徑命名方式。不過XPath 是一種模式(Pattern),可以選出 XML檔案中,路徑符合某個模式的所有節點出來。
例如要選catalog底下的cd中所有price元素可以用: /catalog/cd/price如果XPath的開頭是一個斜線(/)代表這是絕對路徑。如果開頭是兩個斜線(//)表示檔中所有符合模式的元素都會被選出來,即使是處於樹中不同的層級也會被選出來。
以下的語法會選出檔中所有叫做cd的元素(在樹中的任何層級都會被選出來):
//cd

選擇未知的元素

使用星號(Wildcards,*)可以選擇未知的元素。
下面這個語法會選出/catalog/cd 的所有子元素: /catalog/cd/*
以下的語法會選出所有catalog的子元素中,包含有price作為子元素的元素。
/catalog/*/price
以下的語法會選出有兩層父節點,叫做price的所有元素。
/*/*/price
以下的語法會選擇出檔中的所有元素。
//*

要注意的是,想要存取不分層級的元素,XPath語法必須以兩個斜線開頭(//),想要存取未知元素才用星號(*),星號只能代表未知名稱的元素,不能代表未知層級的元素。

選擇分支
使用中括弧可以選擇分支。以下的語法從catalog的子元素中取出第一個叫做cd的元素。XPath的定義中沒有第0元素這種東西。
/catalog/cd[1]
以下語法選擇catalog中的最後一個cd元素:(XPathj並沒有定義 first() 這種函式喔,用上例的 [1]就可以取出第一個元素。
/catalog/cd[last()]
以下語法選出含有price子元素的所有/catalog/cd元素。
/catalog/cd[price]
以下語法選出price元素的值等於10.90的所有/catalog/cd元素
/catalog/cd[price=10.90]
以下語法選出price元素的值等於10.90的所有/catalog/cd元素 的price元素
/catalog/cd[price=10.90]/price

選擇一個以上的路徑
使用Or運算元(|)就可以選擇一個以上的路徑。例如:
/catalog/cd/title | catalog/cd/artist
選擇所有title以及artist元素
//title | //artist
選擇所有title以及artist以及price元素
//title | //artist | //price 選擇屬性
在XPath中,除了選擇元素以外,也可以選擇屬性。屬性都是以@開頭。例如選擇檔中所有叫做country的屬性: //@country 選擇所有含有country這個屬性的cd元素: //cd[@country] 以下語法選擇出含有屬性的所有cd元素 //cd[@*] 以下語法選擇出country屬性值為UK的cd元素 //cd[@country='UK']



4.檢視便可以用超連結製作出簡易RSS閱讀器



Document Object Model
XPath

沒有留言:

張貼留言