監(jiān)理公司管理系統(tǒng) | 工程企業(yè)管理系統(tǒng) | OA系統(tǒng) | ERP系統(tǒng) | 造價(jià)咨詢管理系統(tǒng) | 工程設(shè)計(jì)管理系統(tǒng) | 甲方項(xiàng)目管理系統(tǒng) | 簽約案例 | 客戶案例 | 在線試用
X 關(guān)閉

使用XML:將XSLT用于內(nèi)容管理

申請免費(fèi)試用、咨詢電話:400-8352-114

AMTeam.org

使用XML:將XSLT用于內(nèi)容管理
 
--介紹XM,窮人的內(nèi)容管理器


Beno顃 Marchal (bmarchal@pineapplesoft.com)

顧問,Pineapplesoft

2001 年 7 月

這是使用 XML 專欄的第一部分,該專欄帶有相應(yīng)項(xiàng)目代碼,演示了成熟 XML 應(yīng)用程序的演變。在這一專欄中,作者兼軟件顧問 Beno顃 Marchal 介紹了 XM(XSLT Make),一種利用 XML 和 XSLT 的簡單的、負(fù)擔(dān)得起的 Web 發(fā)布內(nèi)容管理解決方案。代碼樣本顯示了 XSLT 封裝器的開發(fā),使得非程序員也能輕松使用??梢酝ㄟ^鏈接獲得 XM 項(xiàng)目代碼。

歡迎來到使用 XML 專欄 — developerWorks 上的一個(gè)新專欄 — 的第一部分。該專欄的前提是,開發(fā)人員最好通過研究代碼來學(xué)習(xí),因此我會隨同專欄一起開發(fā)一系列 XML 項(xiàng)目,這些項(xiàng)目將在幾篇專欄文章中討論。感謝這種形式,這樣我可以解決更大、更現(xiàn)實(shí)的項(xiàng)目,而不是通??赡軆H為一篇文章的情景所構(gòu)思的項(xiàng)目。請注意,您可以在本專欄伙伴站點(diǎn)上找到作為開放源碼項(xiàng)目的演示項(xiàng)目本身(請參閱參考資料)。我期待著這些項(xiàng)目可以隨著你我的使用而不斷發(fā)展,屆時(shí)我會在這里報(bào)告那些更改。

這一形式另一有趣的特性是您可以一直跟蹤項(xiàng)目,從初期到完成。我發(fā)現(xiàn),人從錯(cuò)誤中學(xué)到的如果說不比從有效的解決方案中更多的話,那至少也是差不多的。因?yàn)槭褂? XML 跟蹤的是實(shí)際項(xiàng)目的開發(fā),所以比起在只有一種假設(shè)情況的一篇一次性文章中,我有更多機(jī)會告誡您提防死胡同。我希望將正在進(jìn)行的開放源碼項(xiàng)目和專欄結(jié)合可以讓我們成為更好的開發(fā)人員。

XM:窮人的內(nèi)容管理器

我要完成的第一個(gè)項(xiàng)目是一個(gè)負(fù)擔(dān)得起的 Web 發(fā)布解決方案。管理一個(gè)超過 200 個(gè)頁面的網(wǎng)站的艱難經(jīng)歷是這個(gè)項(xiàng)目的靈感來源。我發(fā)現(xiàn)有一些非常好的工具可以用來管理或小或大的站點(diǎn),但我無法找到介于兩者之間的適當(dāng)解決方案。

如果您的站點(diǎn)有 10 到 20 個(gè)頁面,那么象 HoTMetaL PRO、DreamOA 或 FrontPage(請參閱參考資料)這樣的 HTML 編輯器非常不錯(cuò)。不過,隨著站點(diǎn)不斷成長,以及工作從創(chuàng)建轉(zhuǎn)移到相對昂貴的維護(hù)上,這些工具就證明不那么有效了。許多 HTML 編輯器都不是很適合于維護(hù)不斷增長的站點(diǎn)。例如,它們可能會強(qiáng)迫您手工編輯所有頁面而只是為了重新設(shè)計(jì)一下導(dǎo)航-如果管理數(shù)量在二十幾個(gè)以下的頁面,這樣做只是麻煩一些,但對于數(shù)量更多的站點(diǎn),就要做太多工作了。

領(lǐng)域的另一端是高端發(fā)布解決方案,例如 OpenMarket、Vignette 或 eContent(請參閱參考資料)。它們在管理具有巨大內(nèi)容庫的大型站點(diǎn)方面做得很好,但(總是有“但”)門票很貴,貴得令大多數(shù)超負(fù)荷的網(wǎng)站管理員可能只能夢想擁有這樣的一個(gè)系統(tǒng)。

那么這中間是什么呢?自己開發(fā)的解決方案。許多網(wǎng)站管理員都已轉(zhuǎn)向腳本(JSP、ASP 或 PHP)和數(shù)據(jù)庫的組合以幫助他們應(yīng)付不斷成長的站點(diǎn)。這種方法很有效,但不是沒有缺點(diǎn)。首先,增加了服務(wù)器代價(jià),因此頁面可能裝入得更慢。另外,基于腳本的網(wǎng)站更容易出現(xiàn)錯(cuò)誤,甚至崩潰(當(dāng)然,我是在說自己;錯(cuò)誤不會困擾 您的代碼)。最后,搜索引擎往往不太可能對動(dòng)態(tài)生成的站點(diǎn)建立索引??偟膩碚f,我發(fā)現(xiàn)雖然腳本和數(shù)據(jù)庫可以使網(wǎng)站管理員的生活更輕松,但對于訪問者來說,它們遠(yuǎn)遠(yuǎn)不夠理想。

為了適合有關(guān) XML 的專欄,我提出了在 XML 和 XSLT 上構(gòu)建的一種替代辦法。準(zhǔn)備 DocBook 或另其它 XML 詞匯格式的文檔,然后將它們自動(dòng)轉(zhuǎn)換成 HTML 確實(shí)很容易。自動(dòng)在這里是最重要的詞。目的是加快手工處理,并盡可能地將站點(diǎn)維護(hù)自動(dòng)化。當(dāng)從小規(guī)模轉(zhuǎn)移到工業(yè)規(guī)模的網(wǎng)站管理時(shí),我愿意考慮它。

至少,在理論上說是這樣。實(shí)際上,要在家里嘗試這些,您最好是程序員。象 Xalan 這樣的 XSLT 處理器使用起來確實(shí)還不是那么友好。存在象 Cocoon 這樣的 XML 發(fā)布框架(請參閱參考資料),但它們還是適合于開發(fā)人員的。我對 XM(XSLT Make)期待的目標(biāo)是讓 XSLT 處理器有張友好的面孔。最終,我希望那些管理中型網(wǎng)站的非開發(fā)人員也能使用 XM。

我選擇這個(gè)項(xiàng)目作為使用 XML 的第一個(gè)項(xiàng)目是因?yàn)?,從讀者對最近的一篇 developerWorks 文章 - Managing e-zines with JavaMail and XSLT -反饋的郵件看來,對用于發(fā)布的 XSLT 友好應(yīng)用程序感興趣的人不在少數(shù)。

線路圖

圖 1 概述了 XM 是如何工作的。準(zhǔn)備和發(fā)布網(wǎng)站有三個(gè)階段:

創(chuàng)作:使用 XML 編輯器編寫內(nèi)容,或者從非 XML 來源(例如字處理器)獲取

發(fā)布:將內(nèi)容轉(zhuǎn)換成 HTML

欣賞:在瀏覽器中查看內(nèi)容

圖 1:使用 XM 進(jìn)行 Web 發(fā)布的三個(gè)步驟


我要提醒您注意,XM 生成的大多是靜態(tài) HTML 頁面。目標(biāo)是幫助網(wǎng)站管理員更好管理網(wǎng)站 -不必犧牲原來的性能。要增強(qiáng)性能,大多數(shù)動(dòng)態(tài)生成的網(wǎng)站使用某種形式的高速緩存。我相信靜態(tài) HTML 頁面,到目前為止,是最好的高速緩存策略。

您可以發(fā)現(xiàn),這個(gè)項(xiàng)目相當(dāng)煩瑣,我并沒有計(jì)劃在一篇(甚至兩篇)專欄文章中實(shí)現(xiàn)其全部。和其它任何認(rèn)真的開發(fā)項(xiàng)目一樣, 使用 XML 的項(xiàng)目將在幾個(gè)月內(nèi)不斷成熟起來。但為了給這一部分做出結(jié)論,我仍然希望指出一些我已認(rèn)識到的難點(diǎn)和里程碑。很明顯,我期待著隨著開發(fā)進(jìn)行添加更多內(nèi)容:

為 XSLT 處理器設(shè)計(jì)一個(gè)易于使用的包裝(包括在本專欄中)。

自動(dòng)通過 FTP 將文件上載到 Web 服務(wù)器。

管理文件和那些文件之間的鏈接(一種解決方案可能是讓樣式表讀取目錄)。

支持例如 PDF(Adobe Acrobat 支持的“可移植文檔格式”)、SVG(用于圖像的“可伸縮向量圖形”)、RSS(用于 Web 門戶的“豐富站點(diǎn)摘要”)等多種發(fā)布格式。

在 Web 服務(wù)器本身上存放 XM 以支持協(xié)作編輯。

內(nèi)容管理例解

XM 第一版中的主要挑戰(zhàn)之一在于使它可用于非程序員。我不僅僅指帶有友好按鈕的漂亮用戶界面。我相信許多漂亮按鈕并不能使不友好的解決方案看起來友好;它仍然是不友好的應(yīng)用程序。當(dāng)然,我已嘗試設(shè)計(jì)一種易于理解的可操作方式。

我的第一個(gè)想法是在“Managing e-zines with JavaMail and XSLT”中的配置文件上進(jìn)行構(gòu)建。如果您沒看過那篇文章,我可以告訴您,配置文件控制著應(yīng)用哪些樣式表,以及結(jié)果應(yīng)該怎樣。有關(guān)示例,請參閱清單 1。我測試過許多變體,但無論我嘗試什么,都無法找出足夠友好的方案。

清單 1. rules.xml:創(chuàng)建友好的配置文件的許多嘗試之一

<?xml version="1.0"?>
<rules version="1.0" xmlns="
http://www.ananas.org/2001/xm/rules">
   <!-- apply the style sheet on XML files -->
   <rule extension="xml">
      <apply-stylesheet file="default.xsl"/>
      <copy from="$xslt" to="$target"/>
   </rule>
   <!-- copy GIF files -->
   <rule extension="gif">
      <copy from="$source" to="$target"/>
   </rule>
   <!-- create an XML file with the content of
        freebies/download, style it -->
   <rule directory="freebies/download">
      <ls dir="$current"/>
      <apply-stylesheet file="download.xsl"/>
      <copy from="$xslt" to="$target"/>
   </rule>
</rules>

然后它使我受到了打擊,這樣一個(gè)帶有規(guī)則和變量(例如 $source)的配置文件天生難以使用。它與腳本語言類似,我們都知道,腳本語言是不適合初學(xué)者的。我偶然發(fā)現(xiàn)了一個(gè)更容易使用的解決方案:讓用戶創(chuàng)建一個(gè)模擬最終網(wǎng)站的目錄,并自動(dòng)產(chǎn)生輸出。我將它稱為 內(nèi)容管理例解,這是因?yàn)橛脩魧?shí)際上創(chuàng)建了如何組織網(wǎng)站的示例,而且 XM 不需要更多配置就可以將它變成真正的網(wǎng)站。

骨架類

如果您計(jì)劃按照示例操作,請?jiān)煸L ananas.org,然后下載項(xiàng)目代碼;在繼續(xù)閱讀下去之前需要它。我在本月介紹的代碼不過是 XM 的骨架。它只做最基本的事:遞歸地遍歷源目錄,在此過程中將樣式表應(yīng)用到每個(gè) XML 文件以產(chǎn)生 HTML 格式的網(wǎng)站。

到目前為止,這些類包括:

NotImplementedException 是 Java 標(biāo)準(zhǔn)庫中我一直非常懷念的唯一類。

在開發(fā)過程期間,嘗試調(diào)用還未實(shí)現(xiàn)的代碼或遇到意外(因此是未實(shí)現(xiàn)的)情況并不罕見。我發(fā)現(xiàn),明確說明我還沒有編寫代碼一直以來節(jié)省了我調(diào)試工作所花的大量時(shí)間。

XMException,當(dāng) XM 遇到錯(cuò)誤時(shí),它拋出這個(gè)異常。這個(gè)異??梢郧度肫渌惓?,例如 TransformerException 或 SAXException。

Resources 只是便利手段。它為我提供了對缺省語言環(huán)境中資源的快速訪問。

Messenger 是與抽象錯(cuò)誤和信息消息的接口。當(dāng)前版本的 XM 從命令行運(yùn)行,但我期望構(gòu)建的是 GUI 或基于 Web 的界面。通過將所有消息經(jīng)過 Messenger 傳輸,支持不同界面就容易了。

DefaultMessenger 是打印到 Writer 的 Messenger 缺省實(shí)現(xiàn)。

DirectoryWalker 執(zhí)行實(shí)際的處理。它遞歸地遍歷一組目錄,在這個(gè)過程中應(yīng)用樣式表。

Console 是 XM 的入口點(diǎn)。就象其名稱所揭示的,它是從命令行運(yùn)行的。

Messenger

如果您忘了異常,那么我告訴您,到目前為止 XM 中兩個(gè)最有用的類是 Messenger 和 DirectoryWalker。清單 2 中的 Messenger 定義了與用戶進(jìn)行通信的接口。這個(gè)類模仿了 TrAX 的 ErrorListener 或 SAX 的 ErrorHandler:

error()、fatal() 和 warning() 用于報(bào)告錯(cuò)誤。

progress() 可以用來實(shí)現(xiàn)進(jìn)度欄或另一種向用戶報(bào)告進(jìn)度的機(jī)制。

info() 報(bào)告其它信息。

因?yàn)?XM 主要是一個(gè)非交互式過程,所以我沒有定義任何提示用戶,或者另外接受輸入的方法。

清單 2. Messenger.java: XM 對用戶的接口

package org.ananas.xm;

import java.io.File;

public interface Messager
{
   public void error(XMException e)
      throws XMException;

   public void fatal(XMException e)
      throws XMException;

   public void warning(XMException e)
      throws XMException;

   public void progress(File sourceFile,File resultFile)
      throws XMException;

   public void info(String msg)
      throws XMException;

   public void info(String pattern,Object[] arguments)
      throws XMException;
}

 DirectoryWalker

清單 3 中的 DirectoryWalker 主要是將 XML 文件從源目錄(包括其子目錄中的文件)復(fù)制到目標(biāo)目錄。只有一個(gè)竅門:它在復(fù)制 XML 文件之前對它們應(yīng)用了一個(gè)樣式表。

清單 3. DirectoryWalker.java:有趣的所在

package org.ananas.xm;

import java.io.*;
import java.util.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
import org.ananas.util.NotImplementedException;

public class DirectoryWalker
{
   protected Templates templates;
   protected long lastModified;
   protected String extension;
   protected Messager messager = null;

   public DirectoryWalker(File stylesheetFile,Messager messager)
      throws TransformerConfigurationException
   {
      lastModified = stylesheetFile.lastModified();
      templates = TransformerFactory.newInstance().
                     newTemplates(new StreamSource(stylesheetFile));
      extension = '.' + templates.getOutputProperties().
                           getProperty(OutputKeys.METHOD);
      this.messager = messager;
   }

   public void walk(String source,String target)
      throws IOException, XMException
   {
      walk(new File(source),new File(target));
   }

   protected void walk(File source,File target)
      throws IOException, XMException
   {
      if(source.isDirectory())
      {
         File[] files = source.listFiles(),
                dirs = new File[files.length],
                docs = new File[files.length];
         int idirs = 0,
             idocs = 0;
         for(int i = 0;i < files.length;i++)
         {
            if(files[i].isDirectory())
               dirs[idirs++] = files[i];
            else if(files[i].isFile())
               docs[idocs++] = files[i];
            else
               throw new NotImplementedException("Expecting file or directory");
         }
         if(!(target.exists() && target.isDirectory()))
            if(!target.mkdirs())
               messager.fatal(new XMException(
                  Resources.getString("cannotcreatedirectory"),
                  new Object[] {target.getAbsolutePath()}));
         for(int i = 0;i < idocs;i++)
         {
            String fname = docs[i].getName();
            int pos = fname.lastIndexOf('.');
            String extension = pos != -1 ? fname.substring(pos + 1) : "";
            if(extension.equals("xml"))
            {
               File result = style(docs[i],target);
               messager.progress(docs[i],result);
            }
            // else copy file
         }
         for(int i = 0;i < idirs;i++)
            walk(dirs[i],new File(target,dirs[i].getName()));
      }
      else
         messager.fatal(new XMException(
            Resources.getString("notdirectory"),
            new Object[] {source.getAbsolutePath()}));
   }

   protected File style(File sourceFile,File targetDir)
      throws XMException
   {
        try {
         String resultName = sourceFile.getName();
         int pos = resultName.lastIndexOf('.');
         if(pos != -1)
            resultName = resultName.substring(0,pos);
         resultName += extension;
         File resultFile = new File(targetDir,resultName);
         if(resultFile.exists())
         {
            if(resultFile.lastModified() >= sourceFile.lastModified() &&
               resultFile.lastModified() >= lastModified)
               return null;
         }
         Transformer transformer = templates.newTransformer();
         transformer.transform(new StreamSource(sourceFile),
                               new StreamResult(resultFile));
         return resultFile;
      }
      catch(TransformerException e)
      {
         throw new XMException(e);
      }
   }
}

walk() 遍歷目錄,應(yīng)用樣式表。它是通過列出所有目錄內(nèi)容,然后分成文件和子目錄開始的。接下來,它對每個(gè)文件應(yīng)用樣式,然后為子目錄遞歸地調(diào)用自身。

應(yīng)用樣式表是 style() 方法的職責(zé)。為了可移植性,DirectoryWalker 使用 TrAX(XML 的轉(zhuǎn)換 API)來與 XSLT 處理器交互。到目前為止,我已用 Xalan 測試了代碼(請參閱參考資料)。

未完待續(xù)……

您已經(jīng)可以下載和練習(xí) XM 代碼了。入口點(diǎn)是 org.ananas.xm.Console 類。它只使用兩個(gè)參數(shù):XML 文件所在的源目錄;以及將由 XM 創(chuàng)建的目標(biāo)目錄。確保樣式表名為 rules.xsl,并且它位于當(dāng)前目錄中。

我承認(rèn)當(dāng)前的版本做不了太多事情,只能用它來發(fā)布最簡單的網(wǎng)站。下一專欄將會有趣許多,因?yàn)槲矣?jì)劃添加一種機(jī)制來對文件系統(tǒng)應(yīng)用樣式表。

參考資料

  • 通過單擊本文頂部或底部的討論,可以參加本文的論壇
  • 可以從 ananas.org 下載所有有關(guān)該項(xiàng)目的代碼,并訂閱有關(guān)該項(xiàng)目新聞和公告的郵件列表。
  • XM 分別使用 XalanXerces-J,作為 XML 語法分析器和 XSLT 處理器。
  • 管理小型站點(diǎn)的網(wǎng)站管理員使用例如 SoftQuad HoTMetaL PRO、Macromedia DreamOAMicrosoft FrontPage 這樣的 HTML 編輯器就已足夠。
  • 大型站點(diǎn)應(yīng)當(dāng)轉(zhuǎn)向那些高端內(nèi)容管理解決方案,例如 OpenMarket, VignetteeContent。請注意,OpenMarket 是 IBM 合作伙伴,它在 WebSphere 上提供其內(nèi)容管理解決方案。
  • Managing e-zines with JavaMail and XSLT(第 1 部分和第 2 部分)是另一篇有關(guān)將 XML 和 XSLT 用于發(fā)布的 developerWorks 文章。
  • Cocoon 是一種開放源碼發(fā)布框架;它大量依賴于動(dòng)態(tài)代碼生成。
  • 要了解 XML、XSLT 和 WebSphere 是如何使您的應(yīng)用程序更易于訪問的,請轉(zhuǎn)至 IBM WebSphere Transcoding Publisher V1.1: Extending Web Applications to the Pervasive World。
  • 可以在 developerWorks XML 專區(qū)中找到更多 XML 參考資料。



關(guān)于作者

Beno顃 Marchal 是住在比利時(shí)那慕爾的顧問和作家。他是
XML by Example、 Applied XML SolutionsXML and the Enterprise 的作者。同時(shí)是 Gamelan 的專欄作家。www.marchal.com 上有其最新項(xiàng)目的詳細(xì)信息??梢酝ㄟ^ bmarchal@pineapplesoft.com 與他聯(lián)系。

發(fā)布:2007-03-25 14:25    編輯:泛普軟件 · xiaona    [打印此頁]    [關(guān)閉]
相關(guān)文章:
鄭州OA系統(tǒng)
聯(lián)系方式

成都公司:成都市成華區(qū)建設(shè)南路160號1層9號

重慶公司:重慶市江北區(qū)紅旗河溝華創(chuàng)商務(wù)大廈18樓

咨詢:400-8352-114

加微信,免費(fèi)獲取試用系統(tǒng)

QQ在線咨詢