承載內容的 DOM 節點:LotusScript 的 TextNode、AttributeNode、Comment、CDATASection
上一篇講了在解析後的 DOM 裡四處走動 —— document、元素、node list。但元素多半只是骨架;資料在葉子上:標籤裡的文字、屬性的值、一段註解、一塊 CDATA。這些是承載內容的節點,而它們的 LotusScript 繫結有一個大到足以讓你第一次嘗試就壞掉的意外。
如果你從 JavaScript 或 Java 過來,你會去找 .data、.length、.name、.value —— 它們一個都不存在。Domino 的 LotusScript DOM 是透過繼承來的 NodeValue 來讀字元內容、透過 AttributeName / AttributeValue 來讀屬性名稱與值。用 W3C 的名字,程式編不過。
重點摘要
- 用
NodeValue讀 text/comment/CDATA 內容(繼承自NotesDOMNode)。這個繫結裡沒有Data、也沒有Length屬性。 - 屬性的名稱與值是
AttributeName(唯讀)與AttributeValue(可讀寫) —— 不是Name/Value。另有IsSpecified(在來源裡有沒有被明確設值?)。 - 繼承:
NotesDOMCharacterDataNode是 TextNode、CommentNode、CDATASectionNode 的抽象基底;CDATASection 衍生自 Text;AttributeNode 直接掛在NotesDOMNode上,不在 CharacterData 底下。 - CharacterData 編輯方法:
SubstringData、AppendData、InsertData、DeleteData、ReplaceData。 NotesDOMTextNode多了SplitText——「在指定 offset 把這個節點切成兩個節點,兩個都留在樹裡當 sibling」。CDATASection 繼承它;Comment 沒有。- 全部 Release 6 新增、不支援 COM。
繼承關係,以及它為什麼重要
NotesDOMNode├─ NotesDOMCharacterDataNode (抽象 — 不直接建立)│ ├─ NotesDOMTextNode│ │ └─ NotesDOMCDATASectionNode│ └─ NotesDOMCommentNode└─ NotesDOMAttributeNode (自成一格 — 不是 character data)NotesDOMCharacterDataNode ——「代表一個 DOM 節點裡的字元資料」—— 是抽象的:「你不建立 NotesDOMCharacterDataNode 物件。而是用衍生節點類別來建立特定物件。」它的價值在於 Text、Comment、CDATASection 都從它繼承同一組五個編輯方法。那條兩層的鏈(CDATASection → Text → CharacterData)代表一個 CDATA 節點也有 SplitText,而 Comment 沒有。而 NotesDOMAttributeNode 刻意擺在旁邊 —— 屬性雖然像字元、卻不是 CharacterDataNode,所以它有自己的 AttributeName / AttributeValue 存取子,而非那組編輯方法。
讀內容 —— 透過 NodeValue
當你巡覽到一個 text、comment 或 CDATA 節點時,內容在 NodeValue 裡:
Select Case node.NodeTypeCase DOMNODETYPE_TEXT_NODE ' 3 Print "Text: [" & node.NodeValue & "]"Case DOMNODETYPE_COMMENT_NODE ' 8 Print "Comment: " & node.NodeValueCase DOMNODETYPE_CDATASECTION_NODE ' 4 Print "CDATA: " & node.NodeValueEnd Select官方 walk-tree 範例正是這樣讀的 —— 三種都用 node.NodeValue。沒有 .Data;去找它,是大家把 DOM 程式搬進 LotusScript 時撞到的第一名編譯錯誤。
屬性 —— AttributeName / AttributeValue
屬性節點來自元素的 Attributes map。用型別專屬的存取子讀它(繼承來的 NodeName / NodeValue 也行,官方範例就是用這個):
Dim attrs As NotesDOMNamedNodeMapSet attrs = element.AttributesDim i As IntegerFor i = 1 To attrs.NumberOfEntries Dim a As NotesDOMAttributeNode Set a = attrs.GetItem(i) Print a.AttributeName & " = " & a.AttributeValue If Not a.IsSpecified Then Print " (預設值,不在來源裡)"NextIsSpecified「指出這個屬性在原始文件裡有沒有被明確賦值」—— 當 DTD 提供預設值、而你想知道哪些屬性是作者真的寫出來的時候很有用。注意缺了什麼:沒有 Name、沒有 Value、沒有 Specified、沒有 OwnerElement。這個繫結是 W3C DOM 的「改名子集」,不是它的鏡像。
編輯字元資料
那五個 CharacterData 方法讓你就地操作內容 —— 在重新序列化之前轉換一棵解析後的樹時很有用:
' textNode 目前裝著 "Hello"Call textNode.AppendData(" World") ' -> "Hello World"Call textNode.InsertData(5, ",") ' -> "Hello, World"Print textNode.SubstringData(0, 5) ' -> "Hello"Call textNode.DeleteData(5, 1) ' 移除逗號Call textNode.ReplaceData(0, 5, "Howdy") ' -> "Howdy World"
' 只有 Text 有:在 offset 5 切成兩個 sibling text 節點Dim tail As NotesDOMTextNodeSet tail = textNode.SplitText(5)SplitText 是 Text 獨有(並由 CDATASection 繼承)的方法 —— Comment 節點沒有它,因為把一段註解切成兩半在樹的語意上沒有意義。
這是 Domino DOM 節點類別三篇系列的第二篇 —— 第一篇講導覽,第三篇講 DTD/宣告層與 SAX 錯誤處理。
同類別在其他語言
跟導覽類別一樣,沒有乾淨的 Domino-API 對應可指 —— Java 與 XPages 的 XML DOM 工作走的是標準 org.w3c.dom 介面(Text、Attr、Comment、CDATASection),不是 lotus.domino 類別。那些 W3C 介面值得認識,正因為它們就是這個 LotusScript 繫結改名前的名字:W3C 叫 Node.getNodeValue() / CharacterData.getData() / Attr.getValue() 的,在這裡收斂成 NodeValue 與 AttributeValue。所以 relatedJava 與 relatedSsjs 留空 —— 那些語言裡的對應物是平台 DOM,不是 Domino 物件。