# 實戰 Groovy: 構建和解析 XML
_簡易 XML 操作_
通過本文,您將了解使用 Groovy 分解 XML 是多么地容易。在本期的 [_實戰 Groovy_](http://www.ibm.com/developerworks/cn/java/j-pg/) 中,作者 Scott Davis 演示了無論您是使用 `MarkupBuilder` 和 `StreamingMarkupBuilder` 創建 XML,還是使用 `XmlParser` 和 `XmlSlurper` 解析 XML,Groovy 都提供了一系列用于處理這類流行數據格式的工具。
XML 似乎已經由來已久。實際上,XML 在 2008 年迎來了它的 10 年慶典(參見 [參考資料](#resources))。由于 Java? 語言只比 XML 早幾年出現,因此有人認為對于 Java 開發人員來說,XML _是_ 始終存在的。
## 關于本系列
Groovy 是在 Java 平臺上運行的一種現代編程語言。它提供與已有 Java 代碼的無縫集成,同時引入了各種生動的新特性,比如說閉包和元編程。簡單來講,Groovy 是 Java 語言的 21 世紀版本。
將任何新工具整合到開發工具包中的關鍵是知道何時使用它以及何時將它留在工具包中。Groovy 的功能可以非常強大,但惟一的條件是正確應用于適當的場景。因此,[_實戰 Groovy_](http://www.ibm.com/developerworks/cn/java/j-pg/) 系列將探究 Groovy 的實際應用,以便幫助您了解何時以及如何成功使用它們。
Java 語言創始人 Sun Microsystems 一直是 XML 的積極支持者。畢竟,XML 的平臺獨立性承諾能與 Java 語言的 “編寫一次,隨處運行” 的口號完美契合。由于這兩種技術具備一些相同的特性,您可能會認為 Java 語言和 XML 能很好地相處。事實上,在 Java 語言中解析和生成 XML 不但奇特而且還復雜。
幸運的是,Groovy 引入了一些全新的、更加合理的方法來創建和處理 XML。在一些示例的幫助下(均可通過 [下載](#download) 獲取),本文向您展示了如何通過 Groovy 簡化 XML 的構建和解析。
## 比較 Java 和 Groovy XML 解析
在 “[for each 剖析](http://www.ibm.com/developerworks/cn/java/j-pg04149.html)” 的結束部分,我提供了一個如清單 1 所示的簡單 XML 文檔。(這次,我添加了 `type` 屬性,稍微增加了它的趣味性。)
##### 清單 1\. XML 文檔,其中列出了我知道的語言
```
<langs type="current">
<language>Java</language>
<language>Groovy</language>
<language>JavaScript</language>
</langs>
```
在 Java 語言中解析這個簡單的 XML 文檔卻絲毫不簡單,如清單 2 所示。它使用了 30 行代碼來解析 5 行 XML 文件。
##### 清單 2\. 在 Java 中解析 XML 文件
```
import org.xml.sax.SAXException;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.IOException;
public class ParseXml {
public static void main(String[] args) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse("src/languages.xml");
//print the "type" attribute
Element langs = doc.getDocumentElement();
System.out.println("type = " + langs.getAttribute("type"));
//print the "language" elements
NodeList list = langs.getElementsByTagName("language");
for(int i = 0 ; i < list.getLength();i++) {
Element language = (Element) list.item(i);
System.out.println(language.getTextContent());
}
}catch(ParserConfigurationException pce) {
pce.printStackTrace();
}catch(SAXException se) {
se.printStackTrace();
}catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
```
比較清單 2 中的 Java 代碼和清單 3 中相應的 Groovy 代碼:
##### 清單 3\. 在 Groovy 中解析 XML
```
def langs = new XmlParser().parse("languages.xml")
println "type = ${langs.attribute("type")}"
langs.language.each{
println it.text()
}
//output:
type = current
Java
Groovy
JavaScript
```
Groovy 代碼最出色的地方并不是它要比相應的 Java 代碼簡短很多 — 雖然使用 5 行 Groovy 代碼解析 5 行 XML 是一個壓倒性的優勢。Groovy 代碼最讓我欣喜的一個地方就是它更具表達性。在編寫 `langs.language.each` 時,我的感覺就像是在直接操作 XML。在 Java 版本中,您再也看不到 XML。
* * *
## 字符串變量和 XML
當您將 XML 存儲在 `String` 變量而不是文件中時,在 Groovy 中使用 XML 的好處會變得更加明顯。Groovy 的三重引號(在其他語言中通常稱作 `HereDoc`)使得在內部存儲 XML 變得非常輕松,如清單 4 所示。這與清單 3 中的 Groovy 示例之間的惟一區別就是將 `XmlParser` 方法調用從 `parse()`(它處理 `File`、`InputStreams`、`Reader` 和 `URI`)切換到 `parseText()`。
##### 清單 4\. 將 XML 存儲在 Groovy 內部
```
def xml = """
<langs type="current">
<language>Java</language>
<language>Groovy</language>
<language>JavaScript</language>
</langs>
"""
def langs = new XmlParser().parseText(xml)
println "type = ${langs.attribute("type")}"
langs.language.each{
println it.text()
}
```
注意,三重引號可以輕松地處理多行 XML 文檔。`xml` 變量是一個真正的普通舊式(plain-old)`java.lang.String` — 您可以添加 `println xml.class` 自己進行驗證。三重引號還可以處理 `type="current"` 的內部引號,而不會強制您像在 Java 代碼中那樣使用反斜杠字符手動進行轉義。
比較清單 4 中簡潔的 Groovy 代碼與清單 5 中相應的 Java 代碼:
##### 清單 5\. 在 Java 代碼內部存儲 XML
```
import org.xml.sax.SAXException;
import org.w3c.dom.*;
import javax.xml.parsers.*;
import java.io.*;
public class ParseXmlFromString {
public static void main(String[] args) {
String xml = "<langs type=\"current\">\n" +
" <language>Java</language>\n" +
" <language>Groovy</language>\n" +
" <language>JavaScript</language>\n" +
"</langs>";
byte[] xmlBytes = xml.getBytes();
InputStream is = new ByteArrayInputStream(xmlBytes);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(is);
//print the "type" attribute
Element langs = doc.getDocumentElement();
System.out.println("type = " + langs.getAttribute("type"));
//print the "language" elements
NodeList list = langs.getElementsByTagName("language");
for(int i = 0 ; i < list.getLength();i++) {
Element language = (Element) list.item(i);
System.out.println(language.getTextContent());
}
}catch(ParserConfigurationException pce) {
pce.printStackTrace();
}catch(SAXException se) {
se.printStackTrace();
}catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
```
注意,`xml` 變量受到了針對內部引號和換行符的轉義字符的污染。然而,更糟的是需要將 `String` 轉換成一個 `byte` 數組,然后再轉換成 `ByteArrayInputStream` 才能進行解析。`DocumentBuilder` 未提供將簡單 `String` 作為 XML 解析的直觀方法。
* * *
## 通過 `MarkupBuilder` 創建 XML
Groovy 相對 Java 語言最大的優勢體現于在代碼中創建 XML 文檔。清單 6 顯示了創建 5 行 XML 代碼段所需的 50 行 Java 代碼:
##### 清單 6\. 使用 Java 代碼創建 XML
```
import org.w3c.dom.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
public class CreateXml {
public static void main(String[] args) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.newDocument();
Element langs = doc.createElement("langs");
langs.setAttribute("type", "current");
doc.appendChild(langs);
Element language1 = doc.createElement("language");
Text text1 = doc.createTextNode("Java");
language1.appendChild(text1);
langs.appendChild(language1);
Element language2 = doc.createElement("language");
Text text2 = doc.createTextNode("Groovy");
language2.appendChild(text2);
langs.appendChild(language2);
Element language3 = doc.createElement("language");
Text text3 = doc.createTextNode("JavaScript");
language3.appendChild(text3);
langs.appendChild(language3);
// Output the XML
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter sw = new StringWriter();
StreamResult sr = new StreamResult(sw);
DOMSource source = new DOMSource(doc);
transformer.transform(source, sr);
String xmlString = sw.toString();
System.out.println(xmlString);
}catch(ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
}
}
```
我知道一些人會立刻抱怨。許多第三方庫都可以簡化此代碼 — JDOM 和 dom4j 是其中最流行的兩個。但是,任何 Java 庫都無法與使用 Groovy `MarkupBuilder` 的簡潔性相比,如清單 7 所示:
##### 清單 7\. 使用 Groovy 創建 XML
```
def xml = new groovy.xml.MarkupBuilder()
xml.langs(type:"current"){
language("Java")
language("Groovy")
language("JavaScript")
}
```
注意到這與 XML 代碼的比率又重新回到了將近 1:1。更加重要的是,我可以再次查看 XML。當然,尖括號已經被替換為大括號,并且屬性使用冒號(Groovy 的 `HashMap` 符號)而不是等號,但其基本結構在 Groovy 或 XML 中都是可以辨認的。它幾乎類似于一個用于構建 XML 的 DSL,您認為呢?
Groovy 能夠實現這種 `Builder` 魔法,因為它是一種動態的語言。另一方面,Java 語言則是靜態的:Java 編譯器將確保所有方法在您調用它們之前都是確實存在的。(如果您嘗試調用不存在的方法,Java 代碼甚至不進行編譯,更不用說運行了。)但是,Groovy 的 `Builder` 證明,某種語言中的 bug 正是另一種語言的特性。如果您查閱 API 文檔中的 `MarkupBuilder` 相關部分,您會發現它沒有 `langs()` 方法、`language()` 方法或任何其他元素名稱。幸運的是,Groovy 可以捕獲這些不存在的方法調用,并采取一些有效的操作。對于 `MarkupBuilder` 的情況,它使用 phantom 方法調用并生成格式良好的 XML。
清單 8 對我剛才給出的簡單的 `MarkupBuilder` 示例進行了擴展。如果您希望在 `String` 變量中捕獲 XML 輸出,則可以傳遞一個 `StringWriter` 到 `MarkupBuilder` 的構造函數中。如果您希望添加更多屬性到 `langs` 中,只需要在傳遞時使用逗號將它們分開。注意,`language` 元素的主體是一個沒有前置名稱的值。您可以在相同的逗號分隔的列表中添加屬性和主體。
##### 清單 8\. 經過擴展的 `MarkupBuilder` 示例
```
def sw = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(sw)
xml.langs(type:"current", count:3, mainstream:true){
language(flavor:"static", version:"1.5", "Java")
language(flavor:"dynamic", version:"1.6.0", "Groovy")
language(flavor:"dynamic", version:"1.9", "JavaScript")
}
println sw
//output:
<langs type='current' count='3' mainstream='true'>
<language flavor='static' version='1.5'>Java</language>
<language flavor='dynamic' version='1.6.0'>Groovy</language>
<language flavor='dynamic' version='1.9'>JavaScript</language>
</langs>
```
通過這些 `MarkupBuilder` 技巧,您可以實現一些有趣的功能。舉例來說,您可以快速構建一個格式良好的 HTML 文檔,并將它寫出到文件中。清單 9 顯示了相應的代碼:
##### 清單 9\. 通過 `MarkupBuilder` 構建 HTML
```
def sw = new StringWriter()
def html = new groovy.xml.MarkupBuilder(sw)
html.html{
head{
title("Links")
}
body{
h1("Here are my HTML bookmarks")
table(border:1){
tr{
th("what")
th("where")
}
tr{
td("Groovy Articles")
td{
a(href:"http://ibm.com/developerworks", "DeveloperWorks")
}
}
}
}
}
def f = new File("index.html")
f.write(sw.toString())
//output:
<html>
<head>
<title>Links</title>
</head>
<body>
<h1>Here are my HTML bookmarks</h1>
<table border='1'>
<tr>
<th>what</th>
<th>where</th>
</tr>
<tr>
<td>Groovy Articles</td>
<td>
<a href='http://ibm.com/developerworks'>DeveloperWorks</a>
</td>
</tr>
</table>
</body>
</html>
```
圖 1 顯示了清單 9 所構建的 HTML 的瀏覽器視圖:
##### 圖 1\. 呈現的 HTML

* * *
## 使用 `StreamingMarkupBuilder` 創建 XML
`MarkupBuilder` 非常適合用于同步構建簡單的 XML 文檔。對于更加高級的 XML 創建,Groovy 提供了一個 `StreamingMarkupBuilder`。通過它,您可以添加各種各樣的 XML 內容,比如說處理指令、名稱空間和使用 `mkp` 幫助對象的未轉義文本(非常適合 `CDATA` 塊)。清單 10 展示了有趣的 `StreamingMarkupBuilder` 特性:
##### 清單 10\. 使用 `StreamingMarkupBuilder` 創建 XML
```
def comment = "<![CDATA[<!-- address is new to this release -->]]>"
def builder = new groovy.xml.StreamingMarkupBuilder()
builder.encoding = "UTF-8"
def person = {
mkp.xmlDeclaration()
mkp.pi("xml-stylesheet": "type='text/xsl' href='myfile.xslt'" )
mkp.declareNamespace('':'http://myDefaultNamespace')
mkp.declareNamespace('location':'http://someOtherNamespace')
person(id:100){
firstname("Jane")
lastname("Doe")
mkp.yieldUnescaped(comment)
location.address("123 Main")
}
}
def writer = new FileWriter("person.xml")
writer << builder.bind(person)
//output:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type='text/xsl' href='myfile.xslt'?>
<person id='100'
xmlns='http://myDefaultNamespace'
xmlns:location='http://someOtherNamespace'>
<firstname>Jane</firstname>
<lastname>Doe</lastname>
<![CDATA[<!-- address is new to this release -->]]>
<location:address>123 Main</location:address>
</person>
```
注意,`StreamingMarkupBuilder` 直到您調用 `bind()` 方法時才會生成最終的 XML,該方法將接受標記和所有指令。這允許您異步構建 XML 文檔的各個部分,并同時輸出它們。(參見 [參考資料](#resources) 了解更多信息。)
* * *
## 理解 `XmlParser`
Groovy 為您提供了兩種生成 XML — `MarkupBuilder` 和 `StreamingMarkupBuilder` 的方式 — 它們分別具備不同的功能。解析 XML 也同樣如此。您可以使用 `XmlParser` 或者 `XmlSlurper`。
`XmlParser` 提供了更加以程序員為中心的 XML 文檔視圖。如果您習慣于使用 `List` 和 `Map`(分別對應于 `Element` 和 `Attribute`)來思考文檔,則應該能夠適應 `XmlParser`。清單 11 稍微解析了 `XmlParser` 的結構:
##### 清單 11\. `XmlParser` 詳細視圖
```
def xml = """
<langs type='current' count='3' mainstream='true'>
<language flavor='static' version='1.5'>Java</language>
<language flavor='dynamic' version='1.6.0'>Groovy</language>
<language flavor='dynamic' version='1.9'>JavaScript</language>
</langs>
"""
def langs = new XmlParser().parseText(xml)
println langs.getClass()
// class groovy.util.Node
println langs
/*
langs[attributes={type=current, count=3, mainstream=true};
value=[language[attributes={flavor=static, version=1.5};
value=[Java]],
language[attributes={flavor=dynamic, version=1.6.0};
value=[Groovy]],
language[attributes={flavor=dynamic, version=1.9};
value=[JavaScript]]
]
]
*/
```
注意,`XmlParser.parseText()` 方法返回了一個 `groovy.util.Node` — 在本例中是 XML 文檔的根 `Node`。當您調用 `println langs` 時,它會調用 `Node.toString()` 方法,以便返回調試輸出。要獲取真實數據,您需要調用 `Node.attribute()` 或者 `Node.text()`。
* * *
## 使用 `XmlParser` 獲取屬性
如前所述,您可以通過調用 `Node.attribute("key")` 來獲取單獨的屬性。如果您調用 `Node.attributes()`,它會返回包含所有 `Node` 的屬性的 `HashMap`。使用您在 “[for each 剖析](http://www.ibm.com/developerworks/cn/java/j-pg04149.html)” 一文中所掌握的 `each` 閉包,遍歷每個屬性簡直就是小菜一碟。清單 12 顯示了一個相應的例子。(參見 [參考資料](#resources),獲取關于 `groovy.util.Node` 的 API 文檔。)
##### 清單 12\. `XmlParser` 將屬性作為 HashMap 對待
```
def langs = new XmlParser().parseText(xml)
println langs.attribute("count")
// 3
langs.attributes().each{k,v->
println "-" * 15
println k
println v
}
//output:
---------------
type
current
---------------
count
3
---------------
mainstream
true
```
與操作屬性相類似,`XmlParser` 為處理元素提供了更好的支持。
* * *
## 使用 `XmlParser` 獲取元素
`XmlParser` 提供了一種直觀的查詢元素的方法,稱作 `GPath`。(它與 `XPath` 類似,僅在 Groovy 中得到了實現。)舉例來說,清單 13 演示了我之前使用的 `langs.language` 結構返回了包含查詢結構的 `groovy.util.NodeList`。`NodeList` 擴展了 `java.util.ArrayList`,因此它基本上就是一個賦予了 `GPath` 超級權限的 `List`。
##### 清單 13\. 使用 `GPath` 和 `XmlParser` 進行查詢
```
def langs = new XmlParser().parseText(xml)
// shortcut query syntax
// on an anonymous NodeList
langs.language.each{
println it.text()
}
// separating the query
// and the each closure
// into distinct parts
def list = langs.language
list.each{
println it.text()
}
println list.getClass()
// groovy.util.NodeList
```
當然,`GPath` 是對 `MarkupBuilder` 的補充。它所采用的技巧與調用不存在的 phantom 方法相同,區別僅在于它用于查詢已有的 XML 而不是動態地生成 XML。
知道 `GPath` 查詢的結果是 `List` 之后,您可以讓您的代碼更加簡練。Groovy 提供了一個 spread-dot 運算符。在單行代碼中,它基本上能迭代整個列表并對每個項執行方法調用。結果將作為 `List` 返回。舉例來說,如果您只關心對查詢結果中的各個項調用 `Node.text()` 方法,那么清單 14 展示了如何在一行代碼中實現它:
##### 清單 14\. 結合 spread-dot 運算符與 `GPath`
```
// the long way of gathering the results
def results = []
langs.language.each{
results << it.text()
}
// the short way using the spread-dot operator
def values = langs.language*.text()
// [Java, Groovy, JavaScript]
// quickly gathering up all of the version attributes
def versions = langs.language*.attribute("version")
// [1.5, 1.6.0, 1.9]
```
和功能強大的 `XmlParser` 一樣,`XmlSlurper` 也實現了更高級別的處理。
* * *
## 使用 `XmlSlurper` 解析 XML
在 [清單 2](#listing2) 中,我說過 Groovy 給我的感覺是在直接操作 XML。`XmlParser` 的功能相當不錯,但它只允許您以編程的方式來操作 XML。您可以使用由 `Node` 組成的 `List` 以及由 `Attribute` 組成的 `HashMap`,并且仍然需要調用 `Node.attribute()` 和 `Node.text()` 等方法才能獲取核心數據。`XmlSlurper` 將刪除方法調用的最后痕跡,讓您感覺就像是在直接處理 XML。
從技術上說,`XmlParser` 返回 `Node` 和 `NodeList`,而 `XmlSlurper` 返回一個 `groovy.util.slurpersupport.GPathResult`。但既然您已經知道,因此我希望您能忘記之前提到的 `XmlSlurper` 的實現細節。如果您能忽略其內部原理,那么將更好地領略其魔力。
清單 15 同時展示了一個 `XmlParser` 和一個 `XmlSlurper`:
##### 清單 15\. `XmlParser` 和 `XmlSlurper`
```
def xml = """
<langs type='current' count='3' mainstream='true'>
<language flavor='static' version='1.5'>Java</language>
<language flavor='dynamic' version='1.6.0'>Groovy</language>
<language flavor='dynamic' version='1.9'>JavaScript</language>
</langs>
"""
def langs = new XmlParser().parseText(xml)
println langs.attribute("count")
langs.language.each{
println it.text()
}
langs = new XmlSlurper().parseText(xml)
println langs._cnnew1@count
langs.language.each{
println it
}
```
注意,`XmlSlurper` 忽略了任何方法調用的概念。您并沒有調用 `langs.attribute("count")`,而是調用了 `langs.@count`。`@` 符號是從 `XPath` 借過來的,但其結果是,您感覺像是在直接操作屬性(與調用 `attribute()` 方法相反)。您沒有調用 `it.text()`,而僅僅調用了 `it`。我們假設您希望直接操作元素的內容。
* * *
## 現實中的 `XmlSlurper`
除了 `langs` 和 `language` 之外,這里還提供了實際的 `XmlSlurper` 示例。Yahoo! 以 RSS 提要的方式按 ZIP 碼提供天氣情況信息。當然,RSS 是 XML 中的一種專門術語。在 Web 瀏覽器中鍵入 `http://weather.yahooapis.com/forecastrss?p=80020`。可以隨意將 Broomfield, Colorado 的 ZIP 碼換成您自己的。清單 16 顯示了最終 RSS 提要的簡單版本:
##### 清單 16\. 顯示最新天氣情況的 Yahoo! RSS 提要
```
<rss version="2.0"
xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0"
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#">
<channel>
<title>Yahoo! Weather - Broomfield, CO</title>
<yweather:location city="Broomfield" region="CO" country="US"/>
<yweather:astronomy sunrise="6:36 am" sunset="5:50 pm"/>
<item>
<title>Conditions for Broomfield, CO at 7:47 am MST</title>
<pubDate>Fri, 27 Feb 2009 7:47 am MST</pubDate>
<yweather:condition text="Partly Cloudy"
code="30" temp="25"
date="Fri, 27 Feb 2009 7:47 am MST" />
</item>
</channel>
</rss>
```
您要做的第一件事就是通過編程來使用這個 RSS。創建一個名稱為 weather.groovy 的文件,并添加如清單 17 所示的代碼:
##### 清單 17\. 以編程的方式獲取 RSS
```
def baseUrl = "http://weather.yahooapis.com/forecastrss"
if(args){
def zip = args[0]
def url = baseUrl + "?p=" + zip
def xml = url.toURL().text
println xml
}else{
println "USAGE: weather zipcode"
}
```
在命令行中鍵入 `groovy weather 80020`,確定您可以看到原始 RSS。
此腳本最重要的部分是 `url.toURL().text`。`url` 變量是一個格式良好的 `String`。Groovy 在所有 `String` 中都添加了一個 `toURL()` 方法,用于將它們轉換成 `java.net.URL`。然后,Groovy 在所有 `URL` 中又添加了一個 `getText()` 方法,用于執行 `HTTP GET` 請求并將結構作為 `String` 返回。
現在,您已經將 RSS 存儲在了 `xml` 變量中,并通過 `XmlSlurper` 實現了一些有趣的功能,如清單 18 所示:
##### 清單 18\. 使用 `XmlSlurper` 解析 RSS
```
def baseUrl = "http://weather.yahooapis.com/forecastrss"
if(args){
def zip = args[0]
def url = baseUrl + "?p=" + zip
def xml = url.toURL().text
def rss = new XmlSlurper().parseText(xml)
println rss.channel.title
println "Sunrise: ${rss.channel.astronomy.@sunrise}"
println "Sunset: ${rss.channel.astronomy.@sunset}"
println "Currently:"
println "\t" + rss.channel.item.condition.@date
println "\t" + rss.channel.item.condition.@temp
println "\t" + rss.channel.item.condition.@text
}else{
println "USAGE: weather zipcode"
}
//output:
Yahoo! Weather - Broomfield, CO
Sunrise: 6:36 am
Sunset: 5:50 pm
Currently:
Fri, 27 Feb 2009 7:47 am MST
25
Partly Cloudy
```
`XmlSlurper` 讓您可以自然地處理 XML,不是嗎?您通過直接引用 `<title>` 元素來打印它 — `rss.channel.title`。您使用一個簡單的 `rss.channel.item.condition.@temp` 來去除 `temp` 屬性。這與編程的感覺不同。它更像是在直接操作 XML。
您是否注意到 `XmlSlurper` 甚至忽略了名稱空間?您在構造函數中啟用名稱空間感知,但我很少這樣做。非常簡單,`XmlSlurper` 能像切黃油那樣分解 XML。
* * *
## 結束語
要在如今成為一名成功的開發人員,您需要一系列能簡化 XML 處理的工具。Groovy 的 `MarkupBuilder` 和 `StreamingMarkupBuilder` 可以非常輕松地動態創建 XML。`XmlParser` 能為您提供由 `Element` 組成的 `List` 以及由 `Attribute` 組成的 `HashMap`,并且 `XmlSlurper` 可以讓代碼全部消失,讓您感覺是在直接操作 XML。
如果沒有 Groovy 的動態功能,XML 處理的強大功能將不可能實現。在下一章文章中,我將更加深入地探索 Groovy 的動態特性。您將了解元編程在 Groovy 中的工作原理,從標準 JDK 類(如 `String.toURL()` 和 `List.each()`)中添加的出色方法到您自己添加的自定義方法。在閱讀了這兩篇文章之后,我希望您能充分了解 Groovy 的 實際應用。
* * *
## 下載
| 描述 | 名字 | 大小 |
| --- | --- | --- |
| 本文示例的源代碼 | [j-pg05199.zip](http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=398765&filename=j-pg05199.zip&method=http&locale=zh_CN) | 6KB |
- 實戰 Groovy
- 實戰 Groovy: SwingBuilder 和 Twitter API,第 2 部分
- 實戰 Groovy: SwingBuilder 和 Twitter API,第 1 部分
- 實戰 Groovy: @Delegate 注釋
- 實戰 Groovy: 使用閉包、ExpandoMetaClass 和類別進行元編程
- 實戰 Groovy: 構建和解析 XML
- 實戰 Groovy: for each 剖析
- 實戰 Groovy: Groovy:Java 程序員的 DSL
- 實戰 Groovy: 關于 MOP 和迷你語言
- 實戰 Groovy: 用 curry 過的閉包進行函數式編程
- 實戰 Groovy: Groovy 的騰飛
- 實戰 Groovy: 在 Java 應用程序中加一些 Groovy 進來
- 實戰 Groovy: 用 Groovy 生成器作標記
- 實戰 Groovy: 用 Groovy 打造服務器端
- 實戰 Groovy: 使用 Groovy 模板進行 MVC 編程
- 實戰 Groovy: 用 Groovy 進行 JDBC 編程
- 實戰 Groovy: 用 Groovy 進行 Ant 腳本編程
- 實戰 Groovy: 用 Groovy 更迅速地對 Java 代碼進行單元測試
- alt.lang.jre: 感受 Groovy