前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VBA编写Ribbon Custom UI编辑器04——解析xml

VBA编写Ribbon Custom UI编辑器04——解析xml

作者头像
xyj
发布2020-08-18 11:31:52
1.1K0
发布2020-08-18 11:31:52
举报
文章被收录于专栏:VBA 学习VBA 学习VBA 学习
01

定义结构

目标是要把customUI.xml解析为二维数组,数组的第0行记录的是属性,第一列记录的是元素,其他地方存储的是属性的值。

同时因为xml本身是树形结构的,所以同时也记录下这些信息,这里使用左孩子右兄弟的结构来记录,所以,首先定义需要生成的数据结构:

'属性
Public Type Attri
    Key As String
    value As String
End Type

Public Type Node
    Left As Long '左孩子
    Right As Long '右兄弟
    
    '元素的名称
    XMLItem As String
    HasChild As Boolean
    
    '属性数组
    Attris() As Attri
    '属性的数量
    AttriNum As Long
End Type

Public Type XML
    Nodes() As Node
    'Nodes实际存放的数量
    nNode As Long
End Type

解析xml最终需要返回的就是XML结构体。

02

状态机解析

要从Ribbon xml中解析元素、属性、属性的值,需要逐个去读取xml中的字符,判断状态,然后执行相应的操作。

这种需求非常的适合使用有限状态机的方法来组织代码,将每一个状态都编写成一个独立的函数,能简化代码的编写:

state

Char

Changestate

备注

0

<

1

XML开始,初始化节点

1

非空白

2

开始读取XMLName

/

9

2

空白

3

取出XMLName,开始找属性

>

0

Stack.Push,开始读取Child的XML,设置HasChild属性为True

3

非空白

4

开始读取属性名称

4

=

5

取出属性名称,开始找属性的Value

5

"

6

开始读取属性Value

6

"

7

取出属性Value

7

空白

7

>

0

Stack.Push,开始读取Child的XML,设置HasChild属性为True

/

8

继续找到>

其他字符

4

新的属性

8

>

0

结束1个,结束了一个不会有Child的

9

>

0

结束1个,Stack.Pop,记录弹出的XMLName,Stack.Top = 0可以结束

99

出错状态,不需要做什么

类模块CXML代码:

Private Const INIT_NODE_NUM As Long = 100
Private Const INIT_ATTRI_NUM As Long = 20

Private Const Err_XML As String = "CXML:XML读取出错,这可能是Ribbon customUI.xml 不符合规范."

'要解析的XML文本
Private strXML As String
'指向XML文本下一个要读取的位置
Private pNext As Long
'要返回的XML结构
Private tXML As XML

'记录XML.Nodes的下一个位置
Private pNodeNext As Long
'记录当前正在处理的Node在XML.Nodes中的Index
Private pNode As Long
'记录状态
Private state As Long
'Stack中记录的是XML.Nodes的Index,方便处理父子关系
Private s As CStack
'XML文本长度
Private iStrXMLLen As Long

'解析一个XML文本到XML结构
'sXML   XML文本
'ret    返回的XML结构体
'Return 返回出错信息
Function Decode(sXML As String, ByRef ret As XML) As String
    iStrXMLLen = VBA.Len(sXML)
    
    If iStrXMLLen < 10 Then
        Decode = "CXML:XML太短了"
        Exit Function
    End If
    strXML = sXML
    
    '解析XML,直到超过了文本长度
    Do While pNext < iStrXMLLen
        '使用CallByName调用相应状态的函数
        state = VBA.CallByName(Me, "S" & VBA.CStr(state), VbMethod)
        '99作为出错情况
        If state = 99 Then
            Decode = Err_XML
            tXML.nNode = pNodeNext
            ret = tXML
            Exit Function
        End If
    Loop
    
    tXML.nNode = pNodeNext
    ret = tXML
End Function

'读取下一个字符
Private Function NextChar() As String
    NextChar = VBA.Mid$(strXML, pNext, 1)
    pNext = pNext + 1
End Function

Private Function NewNode() As Node
    ReDim NewNode.Attris(INIT_ATTRI_NUM - 1) As Attri
End Function

'防止数组越界
Private Function pNodeNextAdd() As Long
    pNodeNext = pNodeNext + 1
    If pNodeNext > UBound(tXML.Nodes) Then
        ReDim Preserve tXML.Nodes(pNodeNext * 1.2)
    End If
End Function
Private Function AttriNumAdd(pNode As Long) As Long
    tXML.Nodes(pNode).AttriNum = tXML.Nodes(pNode).AttriNum + 1
    If tXML.Nodes(pNode).AttriNum > UBound(tXML.Nodes(pNode).Attris) Then
        ReDim Preserve tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum * 1.2)
    End If
End Function
'记录树形结构信息
Function SetParent(iParent As Long, iChild As Long) As Long
    Dim i As Long
    
    If tXML.Nodes(iParent).Left = 0 Then
        tXML.Nodes(iParent).Left = iChild
    Else
        i = tXML.Nodes(iParent).Left
        Do Until tXML.Nodes(i).Right = 0
            i = tXML.Nodes(i).Right
        Loop
        tXML.Nodes(i).Right = iChild
    End If
End Function

Private Sub Class_Initialize()
    ReDim tXML.Nodes(INIT_NODE_NUM - 1) As Node
    Set s = New CStack
    s.MaxSize = INIT_NODE_NUM
    
    'String类型开始的下标是1
    pNext = 1
    '0作为root
    pNodeNext = 1
    
    s.Push 0
End Sub

Function S0() As Long
    Do Until NextChar() = "<"
        If pNext > iStrXMLLen Then S0 = 99: Exit Function
    Loop
    
    pNode = pNodeNext
    pNodeNextAdd
    tXML.Nodes(pNode) = NewNode()
    '设置父节点的子节点
    SetParent s.Top, pNode
    
    S0 = 1
End Function
Function S1() As Long
    Dim tmp As String
    
    tmp = NextChar()
    Do Until tmp <> " "
        If pNext > iStrXMLLen Then S1 = 99: Exit Function
        tmp = NextChar()
    Loop
    
    tXML.Nodes(pNode).XMLItem = tmp
    If tmp = "/" Then
        S1 = 9
    Else
        S1 = 2
    End If
End Function
Function S2() As Long
    Dim tmp As String
    
    tmp = NextChar()
    Do Until tmp = " "
        If pNext > iStrXMLLen Then S2 = 99: Exit Function
        
        tXML.Nodes(pNode).XMLItem = tXML.Nodes(pNode).XMLItem & tmp
        tmp = NextChar()
        If tmp = ">" Then
            s.Push pNode
            tXML.Nodes(pNode).HasChild = True
            S2 = 0
            Exit Function
        End If
    Loop
    
    S2 = 3
End Function
Function S3() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp <> " "
        If pNext > iStrXMLLen Then S3 = 99: Exit Function
        
        tmp = NextChar()
    Loop
    tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).Key = tmp
    
    S3 = 4
End Function
Function S4() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp = "="
        If pNext > iStrXMLLen Then S4 = 99: Exit Function
        
        tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).Key = tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).Key & tmp
        tmp = NextChar()
    Loop
    
    S4 = 5
End Function
Function S5() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp = """"
        If pNext > iStrXMLLen Then S5 = 99: Exit Function
        
        tmp = NextChar()
    Loop
    
    S5 = 6
End Function
Function S6() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp = """"
        If pNext > iStrXMLLen Then S6 = 99: Exit Function
        
        tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).value = tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).value & tmp
        tmp = NextChar()
    Loop
    tXML.Nodes(pNode).AttriNum = tXML.Nodes(pNode).AttriNum + 1
    
    S6 = 7
End Function
Function S7() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp <> " "
        If pNext > iStrXMLLen Then S7 = 99: Exit Function
        
        tmp = NextChar()
    Loop
    
    If tmp = ">" Then
        S7 = 0
        tXML.Nodes(pNode).HasChild = True
        s.Push pNode
        
    ElseIf tmp = "/" Then
        S7 = 8
    Else
        S7 = 4
        tXML.Nodes(pNode).Attris(tXML.Nodes(pNode).AttriNum).Key = tmp
    End If
End Function
Function S8() As Long
    Do Until NextChar() = ">"
        If pNext > iStrXMLLen Then S8 = 99: Exit Function
    Loop
    
    S8 = 0
End Function
Function S9() As Long
    Dim tmp As String
    
    tmp = NextChar()
    
    Do Until tmp = ">"
        If pNext > iStrXMLLen Then S9 = 99: Exit Function
        
        tXML.Nodes(pNode).XMLItem = tXML.Nodes(pNode).XMLItem & tmp
        tmp = NextChar()
    Loop
    s.Pop
    
    If s.Top = 0 Then
        S9 = 10
    Else
        S9 = 0
    End If
    
End Function
Function S10() As Long
    'end
    pNext = VBA.Len(strXML)
End Function

Function S99() As Long
    '出错了,什么也不用做
End Function
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 VBA 学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档