撰文: 蔡志展 chihchun@digitalsesame.com 數位芝麻網路公司
XMLC 是一套用來製作呈像物件(Presentation
Object) 的工具與開發環境。XMLC的全名是
XML Complier,它是一套使用 DOM(Document Object Model) 與 Java Servlet 2.2
相容的高階 API。XMLC可以幫你解析 HTML ( Hypertext Markup Language ) 或是
XML (Extensible Markup Language ) 檔案,並轉換成容易讓Java存取的方法(Method),程式設計師可以很直接的使用 DOM
去存取每一個 HTML 檔案。XMLC 提供網站建置者更高階的網頁模板配置技術,每個 HTML
標籤都被幻化成物件,您可以使用物件導向技術分別存取檔案中的每個標籤。
相信已經有許多的讀者在 Web-Based
的系統中擁有許多的經驗。無論是 PHP、ASP、抑或是 CGI 等相關技術。或許你已經開始使用昇陽公司所研發的J2EE技術,甚至早已在你的網站上實作MVC
(Model/View/Controller) Design
Pattern,並且以JSP 作為主要的呈像模型技術(Presentation technology)。我知道,你已經在這個領域中嚐到不少苦頭。在這個世界上,除了 JSP
外,還有許許多多不同的網頁模組技術。事實上,在這篇文章中,我要帶領你檢閱一項先進的網頁呈像技術,Enhydra
的 XMLC。
讓我們重新檢視市場上的主流網頁技術,有絕大的部份均使用直譯式的描述語言(Scripting Language),然而幾乎大部分的描述語言,都是直接在HTML中進行修改。你必須在網頁中加上特殊的標籤,亦或直接就嵌入程式區塊,在網頁真正被瀏覽時,這些程式區塊會被執行,並顯示出正確的頁面。如果你只是製作小巧簡單的網頁(Ex: 留言版、佈告欄等),這倒也不麻煩。但是如果你所製作的是大型的網站。這種程式設計方式可就會讓你大吃苦頭囉。
我們必須客觀的考慮幾個問題。在進行大型網站的建置時,不可能是由美工人員身兼數職的也參予編寫程式的工作,這是一個很現實的問題,通常會撰寫程式的人,不見得會使用美工軟體。美術功力強的網站設計師不見得懂程式撰寫的技術。尤其在建構大型網站時,更需要不同的專長的人員分工合作。
因此網頁設計師和程式設計師是不同的兩個部門,使用不同的工具設計網頁。美術人員通常使用 HTML 編輯器( 像是Microsoft FrontPage、Micromedia的Dreamweaver ),而程式設計師則使用 IDE 或是一般文件編輯器。如果程式設計師更動了網頁,並把網頁加上了程式碼,你只能保佑客戶或老闆以後不需要更動網頁的排版或內容。因為你已經無法再把網頁交回給美術人員再加以編修,因為裡面已經夾雜了大量的程式碼,當你使用網頁編輯器開啟這份網頁文件後,這些程式碼往往被重新移位的亂七八糟。最後只能摸摸鼻子重新再改過程式。而且也很難將商業邏輯程從網頁中切開,所有的邏輯都被包含在網頁中,逐漸的變得難以維護網頁。
前言提到 XMLC 會讀取原始的網頁,並將其編譯為模板,變成為 XML 物件,原本的網頁仍然不會被置換。我們把這樣的優點一一列舉如下:
總而言之,美術人員與客戶可以先行設計前置的網頁,,使用靜態的網頁建構原型網站。同時程式設計師可針對客戶的文件規格先行設計商業邏輯(Business Logic)與資料結構(Data Layer)的程式碼。當第一份原型網頁完成後即可交於程式設計師,程式設計師即可開始下一步的動態網頁邏輯程式(dynamic presentation logic)撰寫。網頁設計師可以繼續對網頁的外貌作更新,然後再把修改過後的資料放進原始碼目錄中,但不至於影響軟體的運作。
如果你是一位患有資訊焦慮症的 IT 從業人員,每天閱覽各式各樣的新鮮技術,你肯定會認同 XML
提供了非常好的電子文件資訊包裝,傳送技術。加上強大的 Java
提供優越的物件導向技術。這兩種強而有力的工具結合起來更是讓程式設計師驚嘆。不過有人笑稱
XML 是 eXpert Markup Language(給專家使用的標記語言),其實 XML 是一種很簡單的標記語言。麻煩的是處理 XML 所使用的程式技術。
你可能會問:『XML 不是應該是一種優良的資料封裝技術嗎?』
是的,XML 目的確實是在建立一種 “meta language”,用來標示具有結構性資訊電子文件的標示語言,你可以使用 DTD (Document Type Definition)定義你自己的 XML 格式。相對於 SGML (Standard Generalized Markup Language),XML
的確是簡潔有力的多了。一個資訊相關科系學生絕對有能力在兩三天內寫出簡單的解析器。不過要處理檔案中的定位、搜尋、修改,可能就沒有那麼容易囉。
W3C (World
Wide Web Consortium) 定義了 DOM API ,DOM 為 HTML
與 XML 文件提供一組標準的物件規範 ,一個標準的 "Model" 使你可以存取並管理這些物件。它是一種跨平台的高階介面,提供程式或
Script 可以動態的存取一份 XML文件的內容、結構和文件風格。XMLC 的目的是把 HTML 檔當作 XML 的一種(事實上,本來就應該是相容的,W3C
也定義了 XHTML,完全相容 XML 規格的 HTML 標籤規格)。
XMLC 就是使用了這樣的 API
實作的工具。XMLC 編譯後的 Java 物件就是一份 XML 文件的鏡射(mirror)。使用 XMLC
是一件簡單的任務,我們無須熟悉 XML 複雜的規格,但在此之前,我們得先了解些觀念。
請你將 DOM 想像為一組樹狀結構,上面有著數個節點,每個節點又有其子節點。這有點像是 UNIX
作業系統中的檔案系統 (File System)。每個節點都有其特別的型態,並且均繼承自
Node 介面。常見的節點有 Document、Text、Element、以及
Attribute 等。這些節點都可以有子節點,XML 文件可以構成一棵完整的 DOM Tree。假設我們有一個 TR
的標籤,這個標籤在DOM中代表的類別是org.w3c.dom.html.HTMLTableRowElement。不同的標籤均屬於不同的類別,並擁有不同的屬性與方法(method)。這些相關的類別訊息,您可以參考 Enhydra
的 JavaDOC文件。
我們簡單的舉一個例子,表一有一組簡單的HTML標籤。
《表一》

當他被轉換成 DOM
的時候就變成像圖一一樣的樹狀圖。
《圖一》

請想像表一中的
“Table” 標籤中包含著所有的節點,因此它是圖一中最上層的元素。其下再包括了
“Tbody”這個標籤,然後才是其他的 “TR”
“TD” 等子元素,最後堆疊起來就變成圖一的模樣。
實際範例:
舉個實際的範例,會容易了解的多。這裡我們來實際寫支小程式,這支程式的目的是抓取 Slashdot.org
上的新聞列出在網頁上。首先,我們必須設計一個簡單的模板,以供我們製作動態網頁使用,請看圖二。
《圖二》

實際的HTML檔,請參考表二。
《表二》
| 1 <HTML> 2 <HEAD><TITLE>Slashdot.org Examples</TITLE></HEAD> 3 <body bgcolor="#FFFFFF"> 4 <CENTER><A href="http://slashdot.org"><img border=0 alt="" src="media/slashdotlg.gif"></a> 5 <TABLE id="table" width="350" cellpadding=0 cellspacing=0 border=0 summary=""> 6 <TR id="row"><TD> 7 <TABLE width="100%" cellpadding=0 cellspacing=0 border=0 summary=""> 8 <TR> 9 <TD valign=top bgcolor="#006666"><IMG src="http://images.slashdot.org/slc.gif" width=13 height=16 alt="" align=top><FONT size=4 color="#FFFFFF" face="arial,helvetica"><B><SPAN id="title">Title</SPAN></B></FONT></TD> 10 </TR> 11 </TABLE> 12 <B>Posted by <span id="author">Author</span> on <span id="time">Time</span></B><BR> 13 <FONT size=2><B>from <span id="department">department</span> dept.</B></FONT><BR> 14 <P><B>( </B> <A id="urlAnchor" HREF=""><B>Read More...</B></A> | <B><A HREF="" id="commentsAnchor"><span id="comments">comments</span></A></B> comments <B>)</B></P> 15 <TR><TD> 16 </TABLE> 17 </center> 18 </body> 19 </html> |
您可以注意到我在其中幾個標籤中都加上了
“ID” 這個屬性,我會在稍後作說明。現在我們使用:
xmlc –dump Slashdot.html
列出這個 HTML
檔的 DOM 樹狀圖。請參考表三。
《表三》
DOM hierarchy:
HTMLDocument:null DocumentType
HTMLHtmlElement: HTML
HTMLHeadElement: HEAD
HTMLMetaElement: META: content='HTML Tidy, see www.w3.org' name='generator'
HTMLTitleElement: TITLE
Text: Slashdot.org Examples
HTMLBodyElement: BODY: bgcolor='#FFFFFF'
HTMLElement: CENTER
HTMLAnchorElement: A: href='http://slashdot.org'
HTMLImageElement: IMG: alt='' border='0' src='media/slashdotlg.gif'
Text:
HTMLTableElement: TABLE: border='0' cellpadding='0' cellspacing='0' id='table' summary='' width='350'
HTMLTableRowElement: TR: id='row'
HTMLTableCellElement: TD
HTMLTableElement: TABLE: border='0' cellpadding='0' cellspacing='0' summary='' width='100%'
HTMLTableRowElement: TR
HTMLTableCellElement: TD: bgcolor='#006666' valign='top'
HTMLImageElement: IMG: align='top' alt='' height='16' src='http://images.slashdot.org/slc.gif' width='13'
HTMLFontElement: FONT: color='#FFFFFF' face='arial,helvetica' size='4'
HTMLElement: B
HTMLElement: SPAN: id='title'
Text: Title
HTMLElement: B
Text: Posted by
HTMLElement: SPAN: id='author'
Text: Author
Text: on
HTMLElement: SPAN: id='time'
Text: Time
HTMLBRElement: BR
HTMLFontElement: FONT: size='2'
HTMLElement: B
Text: from
HTMLElement: SPAN: id='department'
Text: department
Text: dept.
HTMLBRElement: BR
HTMLParagraphElement: P
HTMLElement: B
Text: (
Text:
HTMLAnchorElement: A: href='' id='urlAnchor'
HTMLElement: B
Text: Read More...
Text: |
HTMLElement: B
HTMLAnchorElement: A: href='' id='commentsAnchor'
HTMLElement: SPAN: id='comments'
Text: comments
Text: comments
HTMLElement: B
Text: )
HTMLTableRowElement: TR
HTMLTableCellElement: TD
|
我們所要做的工作抓取回
Slashdot.org 上的新聞,並一一將每則新聞列在網頁上。必須把這個模板中的文字置換成每則新聞的正確資料。在XMLC中置換文字是一件很輕易的事情,有一個小技巧,只須在想置換的文字前後使用”SPAN”這個標籤標記起來。如同表二中第十一行的
“Author” 與 “Time”。而且我們必須為每一個想置換的標籤取一個名字,並在標籤中標上 ID
這個屬性,以便我們抓取。另外我們也必須置換某些連結(Anchor),相同的也在該置換的超連結標籤中標上 ID
這個標籤。
這些前置作業就緒以後,我們可以使用XMLC直接去編譯這些網頁。您可以使用 xmlc –keep
留住該網頁被編譯後的 Java 原始碼。
xmlc –keep Slashdot.html
這些原始碼的長相如同表四:
《表四》
1 /*
2 ************************************
3 * XMLC GENERATED CODE, DO NOT EDIT *
4 ************************************
5 */
6 package org.d11e.test.xmlc.presentation;
7 import org.w3c.dom.*;
8 import org.enhydra.xml.xmlc.XMLCUtil;
9 import org.enhydra.xml.xmlc.XMLCError;
10 import org.enhydra.xml.xmlc.dom.XMLCDomFactory;
11 public class SlashdotHTMLImpl extends org.enhydra.xml.xmlc.html.HTMLObjectImpl implements SlashdotHTML {
12 /**
13 * Field that is used to identify this as an XMLC
14 * generated class. Contains an reference to the
15 * class object.
16 */
17 public static final Class XMLC_GENERATED_CLASS = SlashdotHTMLImpl.class;
18 /**
19 * Field containing CLASSPATH relative name of the source file
20 * that this class was generated from.
21 */
22 public static final String XMLC_SOURCE_FILE = "org/d11e/test/xmlc/presentation/Slashdot.html";
23 /**
24 * Get the element with id
|
您可以看到第三十九行,我們在網頁中所標下的<SPAN
id=”time”> 被編譯置換成
setTextTime(String
text)
因此我們可以直接使用這個物件,這個方法(Method)置換原本的字串。至於<A
id="urlAnchor" HREF=""> 則被置換成
org.w3c.dom.html.HTMLAnchorElement
getElementUrlAnchor()
。有了這些基本的方法(Method)後,我們即可開始撰寫負責顯示的程式碼了。瞧瞧表五
《表五》
.......
.......
---以上略------
27 public class Slashdot implements HttpPresentation {
28 public void run(HttpPresentationComms comms)
29 throws HttpPresentationException {
30 SlashdotHTML dom =
31 (SlashdotHTML)comms.xmlcFactory.create(SlashdotHTML.class);
32 Vector SSObjs = SlashdotStoryStore.getStorys();
33 for ( int i = 0 ; i < SSObjs.size(); i++) {
34 StoryDO storyObj = (StoryDO)SSObjs.get(i);
35 dom.setTextTitle(storyObj.getTitle());
36 dom.setTextAuthor(storyObj.getAuthor());
37 dom.setTextTime(storyObj.getTime());
38 dom.setTextComments(storyObj.getComments());
39 dom.setTextDepartment(storyObj.getDepartment());
40 dom.getElementCommentsAnchor().setHref(storyObj.getUrl() +
41 "&mode=thread&threshold=-1");
42 dom.getElementUrlAnchor().setHref(storyObj.getUrl());
43 dom.getElementTable().appendChild(
44 dom.getElementRow().cloneNode(true));
45 }
46 dom.getElementRow().getParentNode().removeChild(
47 dom.getElementRow());
48 comms.response.writeHTML(dom);
49 }
50 }
|
在 Enhydra
3.0 以後的版本中,支援網頁自動重編譯的功能,這個功能是在系統執行的狀態下,如果網頁被更新,系統會自動重新編譯該網頁。因此在 Enhydra
3.0 後,一個網頁被XMLC所編譯出來的類別是分成介面(Interface)與實作的類別(Implementation)兩種。必須透過 XMLC Factory
去產生該文件物件的實例(Instance)。
這支程式中的
“dom“ 物件就是 Slashdot.html 被編譯後的類別。我們可以從第 32
行看起,其中 SlashdotStoryStore 這個類別是用來抓取 Slashdot.org 上的新聞的,這個類別使用 SAX Api
將取回
Slashdot.org 上的所有新聞,並把每一則新聞放在 Vector 中。取得新聞的物件後,接下來我們要做的事情便是開始把這些訊息放在頁面上。一個簡單的開始,第 35
行到第 39 行,我們用
setTextMethod() 換掉所有的文字。
第四十行,用
getElementCommentsAnchor() 取得該則新聞的評論超連結,請注意,這是一個 HTMLAnchortElement,這個超連結物件你可以使用 setHref
來設定這個超連結的連結網址。以下的
getElementUrlAnchor() 是相同的事情,此處設定的是該則新聞的超連結。
以上的動作都完成後,我們便完成一則新聞的輸出了,但是這只是更動原始的網頁模板而已。下一步我們要做的是,將一排新聞複製一份到這個表格中。第一次聽起來挺瘋狂,不過我們要做的就是將整個
“TD” 以下所包含的所有物件拷貝一份到表格中。第 43 行,我們取得表格這個物件,並使用
appendChild 這個方法(Method)塞進一份 ElementRow,並且將整份 “TD”
中所有的物件一併拷貝進表格中。
好了,以上我們完成了第一則新聞的輸入。在迴圈中,我們會一一的設入每則新聞的值,並”塞”到表格中。等到所有的新聞都複製完成後,我們再把原本用來當作範例的
“TD” 移除,請參考第 47 行。
大工告成,程式執行的結果
《圖三》

如何呢?在撰寫這段程式碼的時候,你一直沒用到 “print” 或是 “echo” 之類的程式去顯示任何的標籤。因此你可以確定這整段網頁都是合法且正確的,更重要的是,不同的瀏覽器之間也可以操作正常。無須再特別檢查程式碼中是否缺少了某些標籤,以至於瀏覽器沒辦法正常讀取。XMLC 除了可以使用在 HTML 檔以外,也可以應用在各種不同無線通訊的介面上,像是 WAP的網頁語言Wml以及PHS iMode的cHTML 等等(數位芝麻hack過的XMLC版本已經支援了WML/cHTML/VoiceXML等數種規格)。因此你可以使用D11E的Enhydra,輕易的製作出跨越各種無線平台的應用程式。
以上是一個非常簡單的範例,不知道您是否感覺出Enhydra XMLC 的呈像模型技術與使用內嵌式直譯語言的差別呢?在筆者截稿之時,Enhydra 也正釋出XMLC 1.3版,新版本的 XMLC 提供更強的功能,除了XML 解譯器(Parser) 更新使用了 Xerces v1.2 以及 Jtidy 八月四日的版本;支援在編譯時使用SSI。最大的改進莫過於加入了Lazy dom API。Lazy dom API 是將模板編譯為靜態的 DOM 文件,程式中的文件都共享一個實例(instance),因此如果文件中並沒有刪減太多的節點,這組新的 API 會大幅的加快程式的速度與效能。
本文中所提到的原始程式均可在http://www.digitalsesame.com/enhydra/xmlc.tar.gz下載,歡迎您使用這支範例程式觀摩、練習,期待這樣的程式開發模式可以讓您獲得前所未有的體驗。
XMLC 官方網站
http://xmlc.enhydra.org
Java Servlet Technology
http://java.sun.com/products/servlet/
Document Object Model (DOM)
http://www.w3.org/dom
W3C (World Wide Web
Consortium) Extensible Markup Language (XML).