NotesXSLTransformer:用 LotusScript 以 XSLT 轉換 XML 與 DXL
一個外部系統丟給你一份 XML — 一批訂單、或一份 HR 名冊 — 而你要它變成 Notes 文件。問題是它的標籤結構跟你的表單欄位對不起來:進來的是 <order><customer>,你的表單要的是 CustName、OrderNo 這些欄位名。你可以寫一支逐欄 parse XML、再逐欄 doc.ReplaceItemValue 的迴圈 —— 或者,你用一張 XSLT stylesheet 把那份外部 XML 直接重塑成符合表單的 DXL,交給 NotesDXLImporter 建文件。做這個重塑的引擎,就是 NotesXSLTransformer —「DXL(Domino XML)資料透過 XSLT 的轉換」。它是 LotusScript XML 家族的最後一員,與 DOM 和 SAX parser 並列,嵌進同一條以 stream 為基礎的 pipeline。同一個引擎也反過來用:把文件匯出成 DXL、再轉成別的系統要的格式送出去。

重點摘要
- 從 session 建立它:
Set xsl = session.CreateXSLTransformer(input, styleSheet, output)— 三個參數都可選(可稍後用SetInput/SetStyleSheet/SetOutput設),然後呼叫Process。 - stylesheet 必須是
NotesStream。 input 可以是 String、NotesStream、或另一個 XML processor(NotesDOMParser/NotesSAXParser/NotesDXLExporter);output 可以是NotesStream、一個 parser、一個NotesDXLImporter、或一個NotesRichTextItem。 - 在交出去之前,別自己讀寫那些以檔案為底的 stream —「你不能在一個關聯到檔案的 NotesStream 物件被用作 XML 輸入或輸出之前,明確地讀或寫它」(檢查
Bytes可以)。 - 錯誤處理是透過
On Error+lsxbeerr.lss常數與唯讀的Log屬性 — 沒有事件。(不同於NotesSAXParser會拋 SAX 事件處理器。) - Pipelining: 一個 XML 程序的輸出可以是下一個的輸入,所以你能把
NotesDXLExporter→NotesXSLTransformer→NotesDXLImporter串起來、不落任何暫存檔。Release 6 新增;不支援 COM。
三 stream 的寫法
日常的形狀是三個 NotesStream — XML 輸入、XSL stylesheet、輸出 — 接進 transformer。這是官方範例(讀 hello.xml、套 hello.xsl、寫 hello.txt):
%INCLUDE "lsxbeerr.lss"
Sub Initialize On Error lsERR_NOTES_XSLT_INPUT_OBJECT GoTo err_IN On Error lsERR_NOTES_XSLT_OUTPUT_OBJECT GoTo err_OUT On Error lsERR_NOTES_XSLT_STYLESHEET_OBJECT GoTo err_SS
Dim session As New NotesSession Dim XML_in As NotesStream, XSL_ss As NotesStream, XML_out As NotesStream
Set XML_in = session.CreateStream If Not XML_in.Open("c:\dxl\hello.xml") Then Exit Sub If XML_in.Bytes = 0 Then Exit Sub ' 檢查 Bytes 是被允許的
Set XSL_ss = session.CreateStream If Not XSL_ss.Open("c:\dxl\hello.xsl") Then Exit Sub ' stylesheet 必須是 stream
Set XML_out = session.CreateStream If Not XML_out.Open("c:\dxl\hello.txt") Then Exit Sub XML_out.Truncate ' 清掉舊輸出
Dim transformer As NotesXSLTransformer Set transformer = session.CreateXSLTransformer(XML_in, XSL_ss, XML_out) transformer.Process Exit Suberr_IN: MessageBox "XSL Input error",, "XSLTransformer" : Exit Suberr_OUT: MessageBox "XSL Output error",, "XSLTransformer" : Exit Suberr_SS: MessageBox "Style Sheet error",, "XSLTransformer" : Exit SubEnd Sub重要的習慣都在這裡:每個以檔案為底的 stream 都打開、但別自己讀寫它(檢查 Bytes = 0 確認輸入存在是唯一的例外)、Truncate 輸出 stream 好清掉舊內容、讓單一個 Process 跑完整個轉換。
錯誤走 Log,不走事件
這是這個類別跟它的 SAX 手足不同、也是一個錯誤假設會浪費時間的地方:NotesXSLTransformer 沒有事件模型。 沒有 XSLT_* 回呼要接。錯誤處理改成兩件事。第一,On Error 配上 %INCLUDE "lsxbeerr.lss" 帶進來的後端錯誤常數 — lsERR_NOTES_XSLT_INPUT_OBJECT、_OUTPUT_OBJECT、_STYLESHEET_OBJECT — 這就是官方範例區分「輸入問題」與「stylesheet 問題」的方式。第二,唯讀的 Log 屬性,「一個由 processor 產生的警告、錯誤、致命錯誤的 XML 表示」,你可以在 Process 之後讀它拿細節;ExitOnFirstFatalError 控制處理是否在第一個致命錯誤就停。如果你原本期待一個 SAX 式的處理器,這一段就是要重讀的。
Pipelining:跳過暫存檔
transformer 之所以把 parser 與 DXL 物件也當作 input/output、不只是 stream,原因是 pipelining —「把操作組合起來,讓一個 XML 程序的輸出成為另一個的輸入」。舉一個具體的:你要把一批文件從舊資料庫搬到一個表單設計不同的新資料庫,其中一個欄位改了名 —— 舊的叫 Cust,新表單要 CustomerName。與其寫 LotusScript 逐份文件複製、逐欄改名,你把三個物件串起來:
NotesDXLExporter(把舊庫文件匯出成 DXL)→NotesXSLTransformer(stylesheet 把<item name='Cust'>改寫成<item name='CustomerName'>)→NotesDXLImporter(把改寫後的 DXL 寫進新庫)。
那支 stylesheet 做的就是這種節點層級的改寫:
<!-- 進來的 --> <item name='Cust'><text>Acme</text></item><!-- 出去的 --> <item name='CustomerName'><text>Acme</text></item>欄位對應寫在宣告式的 stylesheet 裡,不寫在程式流程裡 —— 多一個欄位改名就多一條 template,不動 LotusScript。stylesheet 仍然是一個 NotesStream,但 XML 輸入與輸出變成活的物件,沒有任何中間檔碰到磁碟。AddParameter 讓你把最上層的 <xsl:param> 值傳進 stylesheet,所以同一個轉換能每次跑用不同參數(例如目標表單名、或一個批次代碼)。
同類別在其他語言
| LotusScript | Java | SSJS / XPages |
|---|---|---|
session.CreateXSLTransformer(...) → NotesXSLTransformer | session.createXSLTransformer(...) → XSLTransformer | session.createXSLTransformer(...) |
Java 有一個對應的 lotus.domino.XSLTransformer,但它不是 1:1 鏡像 — Java 的 DXL/XSLT 路線用 JAXP 式的 result-target 模型(XSLTResultTarget,由 FileOutputStream 或 StringWriter 建構),而非那個 stream 三件組。SSJS 透過 session 取到同一個後端工廠方法。核心概念 — 餵 XML/DXL 與一個 stylesheet、拿到轉換後的輸出、優先用 pipelining 而非暫存檔 — 在三種語言都通用。