Các ứng dụng của một XML Parser

XML càng lúc càng trở nên thịnh hành. Dầu muốn hay không, nếu là software engineer, trước sau gì bạn cũng phải lập trình với XML. Nếu lập trình bằng VB6 bạn có thể dùng Document Object Model (DOM)Simple API for XML (SAX) của Microsoft để giúp đở bạn trong công tác parsing (phân tích, sắp đặt) các XML files.
hay

DOM đọc nguyên một XML file rồi parse nó thành một Tree có đẳng cấp trong bộ nhớ, tức là một node cha của Document có những nodes con đại diện cho comments, tags, directives và text (gọi là XML entities).

Trong khi đó SAX đọc một XML file và trong khi parse sẽ generate những Events cho hay khi nào nó gặp phải những XML entities. SAX không tạo ra một Tree nào cả, nên các ứng dụng tùy thuộc vào cách ta handle các Events từ SAX. Dĩ nhiên là SAX nhỏ và đơn giản hơn DOM nhiều.

Ðể không phải tùy thuộc hoàn toàn vào XML parser của người khác và để giúp bạn có ý niệm thực tế về cách làm việc của một XML Parser, trong bài nầy ta sẽ triển khai một XML Parser đơn giản (Simple XML Parser - SXMLParser) hoàn toàn bằng VB6 và áp dụng nó một cách thực tiển để làm mẫu. SXMLParser tuy nhỏ nhưng có những đặc tính tương tợ như SAX và dĩ nhiên bạn có thể tha hồ sửa đổi, thêm những features tùy ý.

Các áp dụng trước mắt là làm đẹp (Pretty) XML code, thêm màu cho XML content khi hiển thị trong một WebBrowser như trong hình dưới đây:

và tạo một Treeview tượng trưng cho DOM:


Có được source code của XML parser của mình bạn sẽ chiếm ưu thế so với người khác khi thiết kế hay deploy program trên mạng.
Trước khi bàn về program nầy ta hãy ôn lại các qui luật căn bản về một Well-Formed XML.

Well-Formed XML

Mặc dù bạn có thể đặt ra bao nhiêu Tag cũng được, nhưng mỗi trang XML cần phải theo một số qui luật để được xem là Well-Formed (có đầu, có đuôi).
Nếu một trang XML không Well-Formed thì coi như xài không đuợc, không có chương trình xử lý nào sẽ chịu làm việc với dữ liệu bên trong của nó. Do đó một trang XML cần phải theo đúng các qui luật sau đây:
  1. Trang XML phải bắt đầu bằng câu tuyên bố XML (XML declaration). Ðiểm nầy ta có thể bỏ qua đuợc.
  2. Mỗi bộ phận, gọi là "element" phải nằm giữa một Tag Pair.
  3. Nếu Tag nào không chứa gì ở giữa thì phải chấm dứt bằng "/>", thí dụ như <BR/> hay <HR/>.
  4. Một trang XML phải có một element độc nhất chứa tất cả các elements khác. Đó là root của tree biểu diễn trang XML.
  5. Các Tag Pair không được xen kẻ nhau (thí dụ như <name>John Stanmore<address>25 King Street</name></address> là bất hợp lệ vì <address> nằm trong Tag Pair name).
và thêm một vài qui luật về cách dùng các mẫu tự đặc biệt. Ngoài ra các Tag Pair phải đánh vần đúng y như nhau kể cả chữ hoa, chữ thường, (thí dụ: <STUDENT> và </Student> là bất hợp lệ) và tất cả giá trị các Attributes đều phải nằm giữa hai ngoặc kép (thí dụ: standalone=yes là bất hợp lệ, phải dùng standalone="yes" mới được.)

Thiết kế SXMLParser

Có một VB6 class chính để lo hầu như hoàn toàn việc parsing một XML file. Sau khi instantiated một Object thuộc Class clsXMLParser, ta chỉ cần cho nó tên của XML file là nó bắt đầu công tác parsing ngay.

Như trong hình màu của XML phía trên ta thấy phần chính của XML là từ hàng thứ tư trở đi khi bắt đầu với Open Tag <library>. Tương ứng với mỗi Open Tag là có một Close Tag, thí dụ như </library>. Bên trong mỗi cặp Tags có thể có những cặp Tags (con) khác.

Một Open Tag có thể chứa nhiều cặp Attributes dưới dạng Name="Value". Lưu ý là Value phải nằm giữa hai dấu ngoặc.

SXMLParser sẽ đi qua từng character một của XML file. Khi đọc xong một Open Tag, thí dụ như:


<book hardback="yes" series="Professional C++">
SXMLParser sẽ Raise một StartElement Event để được handled trong Form chánh bởi Sub XMLParser_StartElement. Event nầy cho Form chánh tên của Tag và một collection của các cặp Name="Value" Attributes, thí dụ như Tag book đầu tiên chứa hardback="yes" series="Professional C++", chẳng hạn.

Trong Sub XMLParser_StartElement ta làm cùng một lúc ba chuyện:
  1. Làm đẹp XML code, tức là các hàng thụt ra, thục vào tùy theo thứ bậc cho dễ đọc.
  2. Thêm màu cho HTML file để hiển thị XML code trong WebBrowser
  3. Tạo các Nodes trong TreeView
Private Sub XMLParser_StartElement(ByVal Name As String, ByVal tagAttributes As clsAttributes) 
   ' A complete Start Element has been processed
   Dim TStr 
   ' Build a string of Atributes' Name="Value" pairs
   TStr = BuildAttributeString(tagAttributes) 
   ' Display Name Tag in Pretty XML Listbox
   lstXML.AddItem Space(XMLParser.NestedLevel * TabWidth) & "<" & Name & TStr & ">" 
   ' Add blue color to the equal sign
   TStr = Replace(TStr, "=", "=") 
   ' prepare colour HTML Name tag
   lstHTML.AddItem Space(XMLParser.NestedLevel * TabWidth) & "<Font color=red><</Font>" _ 
& "<Font color=blue>" & Name & "" & "<Font color=green>" & TStr & "</Font>" & "<Font color=red>></Font>" 
   ' add a node to the Treeview and save its index in the stack of nested nodes
   If XMLParser.NestedLevel = 0 Then 
      ' create the root node
      With XMLTree.Nodes.Add(, , , Name) 
         nodeStack(0) = .Index  ' save the node index in stack
         .Expanded = True  ' Expand node
      End With 
   Else 
      ' create a child node of the higher nested level mode
      With XMLTree.Nodes.Add(nodeStack(XMLParser.NestedLevel - 1), tvwChild, , Name) 
         nodeStack(XMLParser.NestedLevel) = .Index  ' save the node index in stack
         .Expanded = True  ' Expand node
      End With 
   End If End Sub 
Ðể tái tạo hàng Name="Value" cho collection của các Attributes của một Tag ta dùng Function BuildAttributeString như sau:

Function BuildAttributeString(ByVal tagAttributes As clsAttributes) As String 
   ' Build a string of Atributes' Name="Value" pairs for Element or Instruction
   Dim i, TStr 
   Dim attr As clsAttributeItem 
   ' Iterate through each Attribute in the collection
   For i = 1 To tagAttributes.Count 
      ' refer to i-th attribute
      Set attr = tagAttributes.Item(i) 
      ' Start with a space, create string Name="Value"
      TStr = TStr & " " & attr.Name & "=""" & attr.Value & """" 
   Next 
   BuildAttributeString = TStr  ' Return the resultant string
End Function 
Dưới đây là danh sách các Events raised bởi SXMLParser để Form chánh xử lý:

' Start of parsing
Event StartDocument() 
' End of parsing
Event EndDocument() 
' An XML Instruction has been parsed
Event ProcessingInstruction(ByVal Name As String, ByVal tagAttributes As clsAttributes) 
' An XML comment has been parsed
Event Comment(ByVal Text As String) 
' An open tag as been parsed
Event StartElement(ByVal Name As String, ByVal tagAttributes As clsAttributes) 
' A close tag as been parsed
Event EndElement(ByVal Name As String) 
' A block of text has been parsed
Event Characters(ByVal Text As String) 
' Error encountered while parsing
Event ParseError(ByVal ErrorNo As Integer, ByVal Description As String) 
Trong khi parsing SXMLParser thay đổi State hay Mode tùy theo trạng thái nó đang tìm kiếm thứ gì, chẳng hạn như character <, >, Attribute Name, Attribute Value, Close Tag .v.v.. Nếu nó khám phá là XML không Well-Formed thì nó sẽ Raise một ParseError Event với lý do và chi tiết liên hệ về Error ấy để hiển thị trong Form chánh, giúp User biết cần sửa đổi ở đâu trong XML file.

Danh sách các loại Error mà SXMLParser support đuợc liệt kê dưới đây. Xin lưu ý là có khi Error Message không rõ ràng như ta tưởng tượng vì parser không thông minh như chúng ta.

Const cParseEmptyXML = 1 
Const cParseNoCommentCloseTag = 2 
Const cParseNoValueCloseQuote = 3 
Const cParseNoAttributeName = 4 
Const cParseNoEqualSign = 5 
Const cParseNoAttributeValue = 6 
Const cParseNoCDataCloseTag = 7 
Const cParseUnknownSymbols = 8 
Const cParseNoOpenQuote = 9 
Const cParseBadOpenTag = 10 
Const cParseBadCloseTag = 11 
Const cParseMismatchTagName = 12 
Const cParseNoInstructionCloseTag = 13 
Vì các Tag Pairs cần phải có Tag Names giống nhau hoàn toàn (chữ hoa, chữ thường) và không xen kẻ nhau đuợc, nên ta cần có một Stack để chứa các Tag Names theo đúng đẳng cấp trên dưới. Một Stack là một danh sách theo thứ tự Last-In, First-Out, tức là cái gì mới vào nhất sẽ ra đầu tiên.

Ta thực hiện Stack nầy bằng Class clsStack. clsStack chứa các Items thành một String, mà các Items đuợc ngăn cách nhau bởi một vbNullChar (character có ASC value bằng 0). Item mới nhất (Last-In) nằm đầu bên trái của String.

Có ba Functions chánh của Class clsStack là Push (để nhét một TagName vào), Pop (để lấy TagName mới nhất ra) và LastIn (để chỉ xem TagName mới nhất, chớ không lấy ra).

Public Sub Push(InItem As String) 
   ' Push a Item up the Stack.
   ' Remove any vbNullChars in the Item
   ' Use vbNullChar as the Delimiter
   '
   ' Prefix the Item to the Stack string
   mStacks = Replace(InItem, vbNullChar, "") & vbNullChar & mStacks 
   mCount = mCount + 1  ' Increment the Item count
End Sub
 
Public Function Pop() As String 
   ' Remove and return the LastIn Item in the Stack.
   Dim Pos 
   If mCount > 0 Then 
      Pos = InStr(mStacks, vbNullChar)  ' Locate vbNullChar
      If Pos > 0 Then 
         Pop = Left(mStacks, Pos - 1)  ' Extract the LastIn Item
         mStacks = Mid(mStacks, Pos + 1)  ' Keep the remain of the Stack string
         mCount = mCount - 1  ' decrement the Item Count
      End If 
   End If 
End Function
 
Public Function LastIn() As String 
   ' View the LastIn Item in the Stack. Leave Stack unchanged
   Dim Pos 
   If mCount > 0 Then 
      Pos = InStr(mStacks, vbNullChar)  ' Locate vbNullChar
      If Pos > 0 Then 
         LastIn = Left(mStacks, Pos - 1)  ' Extract the LastIn Item
      End If 
   End If 
End Function 


Ðặc biệt trong program nầy, chỉ cần một click duy nhất lên nút Paste, XML text trong Clipboard sẽ được biến thành HTML code để làm đẹp và hiển thị XML code với màu trong WebBrowser. Ngay sau đó bạn có thể Paste content của Clipboard vào một trang Web.
Dưới đây là listing của Sub CmdPaste_Click

Private Sub CmdPaste_Click() 
   ' Parse the Clipboard content and copy the resultant colour HTML back to clipboard
   Dim i, TStr 
   ' Fetch content of clipboard
   TStr = Clipboard.GetText(vbCFText) 
   ' Write it temporarily to "Temp.XML" file in the same folder where this program resides
   WriteTextFile GetLocalDirectory & "Temp.XML", TStr 
   ' Place the XML filename into TextBox txtFilename
   txtFilename.Text = GetLocalDirectory & "Temp.XML" 
   ' Emulating User's action of clicking the commandbutton Parse
   cmdParse_Click 
   ' If there're something as a result, copy everything from the Listbox lstHTML
   ' except for the first and last line, which contain HTML header/footer.
   ' Select the required lines from the Textbox
   If lstHTML.ListCount > 2 Then 
      For i = 1 To lstHTML.ListCount - 2 
         lstHTML.Selected(i) = True 
      Next 
      ' Emulating User's action of clicking the commandbutton Copy
      CmdCopy_Click 
   End If 
End Sub 

No comments:

Post a Comment

Bài đăng mới: