顯示具有 xml 標籤的文章。 顯示所有文章
顯示具有 xml 標籤的文章。 顯示所有文章

2012年9月30日 星期日

Groovy與XML的相遇(三) : 如何找到節點

這一次就不寫 Java Code了,因為用傳統的寫法會發瘋。

一樣,以剛剛那個xml為範例

<root>
  <star age='22' blood='A'>布萊德彼特1</star>
  <star age='26' blood='C'>小勞勃道尼</star>
  <star age='24' blood='B'>基努李維</star>
</root> 

(明明他們就沒那麼年輕…)

問題1:  找到年紀小於25歲的男星,我只要他們的名字

println roots.star.find{  it.@age <=25 }.text() 

下底線又用了閉包傳入。很簡單吧! 可是執行失敗說!
因為xml讀進來都是字串啊,Groovy沒有人工智慧。
好,那怎麼辦…? 你如果有從我第一篇開始看,你應該記得轉型吧。

println roots.star.find{  it.@age as int <=25 }.text() 

你很快樂的按下去,不過結果還是錯的,你不要把Groovy當神好嗎。

println roots.star.find{  (it.@age as int) <=25 }.text() 

加個括號就行了。是那裡面的東西要轉成 int ,後面再來比條件。
問題1的解法,一行結束! 不過剛剛符合條件的男星有二個耶 ?
find 只會傳出第一個 match 的。
findAll 會傳出都符合條件的。於是我們再試一次

List list = roots.star.findAll{  (it.@age as int)  <=25 } as List
 

沒錯,還順便轉了型,裡面裝好了 groovy.util.Node。
List<groovy.util.Node> 啦。

問題2 : 把這些明星的年紀由小到大排序,呃,我要大於23歲的。然後裝成 List<Integer>。

當然以Groovy來說一定要秒殺解決的。


List list2 = (roots.star.@age.findAll{ (it as int ) >=23 } as List).sort()


我把這一行一個一個說明;
  • 取得root下的所有明星節點,但是我要他們的age而已,這時候已經是單純 age 的字串集合了。
  • 從這個結果中,幫我找 age 集合中年紀大於23 歲的,並轉型成 list。
  • 然後再排序。

是不是很方便呢? 其它的進階 xml 密技,就給你自己去研究了 !



Groovy與XML的相遇(二) : 解析文件

按照傳統,還是先來一段 Java code, 31行


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();
    }
  }
}

我不針對上面抄來的程式做修正了,反正大約都是一層的xml簡單文件而已。下面是 Groovy 的寫法:

def roots = new XmlParser().parse("D:\\test.xml")
roots.star.each{   
   println "age="+it.attribute("age") +" " +  it.text() 
}

處理的是這份文件

<root>
  <star age='22' blood='A'>布萊德彼特1</star>
  <star age='26' blood='C'>小勞勃道尼</star>
  <star age='24' blood='B'>基努李維</star>
</root> 

你應該已經知道 it 是預設的,如果你的閉包參數只有一個,那麼it 就是指這個物件。 唯一要注意的就是,如果要取 tag 裡的文字是用 text(),要取參數就是 it.attribute("名稱")。

( 取參數也可以用 it.@age ,如果你開始習慣之後,就可以改成Groovy提供的做法。  )

roots.star.each 這種語法或許 Java 的程序員還不是很習慣,但其實jQuery 裡也大量使用這種觀念啊。

$("#talbe1 tr)".each({

});
是否你覺得似曾相識呢? 我把它換行一下。

$("#talbe1 tr)".each(  {這裡就是一個活生生的閉包啊}   );

再注意一次相同性,都是把閉包 { } 丟到方法each 中做參數。 在這裡閉包就像是一個沒名字的方法。

你還是有一點意見對吧…

roots.star.each{   
   println "age="+it.attribute("age") +" " +  it.text() 
}

你說明明就有一點不同,和jQuery不同啊,少了圓括號與最後的分號。
那我們改一下再來跑一次。

roots.star.each({     
    println "age=" + it.@age +" " +  it.text()   
});
我連分號都加了,這樣你沒話說了吧,還是可以跑啊。 why!!
為何在Groovy 不用加 ( ) 呢? 這是Groovy 提供的一種簡潔式的寫法。好啦,我直接整理給你看三種不同寫法,都可以跑。

roots.star.each({     
     
});
roots.star.each(){     
     
};
roots.star.each{     
    
};
這三種都是一樣的意思。我很快的總結一下

  1. 真正的寫法是如此,閉包是each的參數。 
  2. 為了好看,閉包可以放在each()的後面,因為它太長了嘛! 
  3. 如果each只收一個閉包做為參數,那 ( ) 就可以省了,它是2 的再精簡寫法。

再感覺一下… Groovy 是不是沒那麼奇怪了呢? 是不是常覺得…如果jQuery的寫法可以用在 Java 多好? 如果有這種感覺的話,你才是真的是個程序員!

Groovy 與 XML 的相遇(一) : MarkupBuilder + XmlSlurper

用Groovy 來產生 xml 再簡單不過了。雖然以前的 dom4j也是用物件的方式操作,但你要做的功夫相當的多。 先貼Java要怎麼寫…大約51行

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 root = doc.createElement("root");      
      doc.appendChild(root);

      Element star1 = doc.createElement("star");
      Text text1 = doc.createTextNode("布萊德彼特");
      star1.appendChild(text1);
      root.appendChild(star1);

      Element star2 = doc.createElement("star");
      Text text2 = doc.createTextNode("小勞勃道尼");
      star2.appendChild(text2);
      langs.appendChild(star2);

      Element star3 = doc.createElement("star");
      Text text3 = doc.createTextNode("基努李維");
      star3.appendChild(text3);
      langs.appendChild(star3);

      // 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();
    }
  }
}
雖然非常容易理解,一個一個物件生成,但這麼單純的事,要花51行來寫,覺得你快要死了嗎? (況且他們的屬性我還沒有加進去哩……) 來一點輕鬆的吧。先寫了一個第一版,15行,很棒了吧??

import groovy.xml.MarkupBuilder
import groovy.util.XmlSlurper
def file = new File("d:\\test.xml")
def objs = [
    [ age: 22, name: "布萊德彼特", blood: "A" ],
    [ age: 26, name: "小勞勃道尼", blood: "C" ],
    [ age: 24, name: "基努李維", blood: "B" ] ]
def b = new MarkupBuilder(new FileWriter(file))
b.root {
    objs.each { o ->
        star(age: o.age , blood:o.blood ,  o.name) {          
        }
    }
}
如果不要先把資料弄成集合 ( 雖然這樣比較容易看的懂 ),要寫的行數更少…

def xml = new groovy.xml.MarkupBuilder(new FileWriter("d:\\test.xml"))
xml.root(){
  star(age: 22, blood: "A" , "布萊德彼特")
  star(age: 26, blood: "C" , "小勞勃道尼")
  star(age: 24, blood: "B" , "基努李維")
}

對,你沒看錯,就是6行。 除了行數變少之外,易讀性完全破表。 root 並不是一個死的方法,你將它改成stars,那麼產出的xml的根節點就叫做stars了。裡面所有的東西都能改,只要你結構掌握住了,xml 某一個節點要加新的屬性,易如反掌! 維護程式的人,你覺得他看上面的51行容易理解,還是下面的6行呢?

我們都知道重構的重要。若只是為了把原來就繁複的語法歸類而重構,那為何不一開始就精簡語法? 

這樣精簡的xml 語法,很大的功勞一樣是用閉包做的。程式人員只要專心在資料面,如何產生xml 的部份並非你的價值所在。除非你是開發 xml parser 的人,否則不要沉醉在自己高深的50行程式中。這裡面並沒有價值。再一次強調,就像醫生的價值在於醫好別人的病,不在於你會用幾種手術刀。若你可以空手就取出腫瘤,30秒結束回家吃飯,沒有病人會想抱怨的。