我有一个界面来循环浏览XML子项并对其进行编辑。像这样:
XML文件的外观如下:
<?xml version="1.0"?>
<catalog>
<query id="bk100">
<question>Do we have Docker security?</question>
<answer>Yes</answer>
<comment>None</comment>
<genre>Cloud</genre>
</query>
<query id="bk101">
<question>Do we have cloud security</question>
<answer>Yes</answer>
<comment>None</comment>
<genre>SCPC</genre>
</query>
<query id="bk100">
<question>Do we have Kubernetos security?</question>
<answer>Yes</answer>
<comment>None</comment>
<genre>Cloud</genre>
</query>
</catalog>
我正在Global variabes
中阅读并存储这些孩子:
xmlUrl = ThisWorkbook.Path & "\Blah.xml"
oXMLFile.Load (xmlUrl)
Set QuestionNodes = oXMLFile.SelectNodes("/catalog/query/question/text()")
现在,一旦用户从界面(使用组合框或其他工具)中选择Genre
,例如SCPC
-我希望下一个和上一个按钮允许循环浏览问题和Genre SCPC
因此,例如,“下一步”按钮的伪实现将如下所示:
'Next XML Node Iterartor
Private Sub btnNextEntry_Click()
Interate Where GenreNodes(i).NodeValue = "SCPC"
txtQuestion.Value = QuestionNodes(i).NodeValue
Pause 'When the user clicks Next again, the Next Node Data Is Showed
End Sub
,与此类似,Previous button
也适用。显然,我在这里如何实现这一目标是不合逻辑的。由于我还需要具有编辑和保存功能,因此我认为使用基于索引的迭代是个好主意,但是使用基于Genre
的过滤,它现在变得没有多大意义,我被困住了。 / p>
有什么技巧可以解决这个问题吗?谢谢。
答案 0 :(得分:1)
在问题列表中使用Set QuestionNodes = oXMLFile.SelectNodes("/catalog/query/question/text()")
使得过滤变得比需要困难。使用查询节点列表,然后根据需要访问子节点更加容易。
因此,如果要列出所有节点,请使用:
Dim queryNodes As IXMLDOMNodeList
' ...
Set queryNodes = oXmlFile.SelectNodes("/catalog/query")
然后您可以像下面这样使用子节点的值:
Dim node As IXMLDOMNode
For Each node In queryNodes
Debug.Print "Q: " & node.SelectSingleNode("question").Text & vbCrLf & _
"A: " & node.SelectSingleNode("answer").Text & vbCrLf & _
"C: " & node.SelectSingleNode("comment").Text & vbCrLf & _
"G: " & node.SelectSingleNode("genre").Text & vbCrLf & vbCrLf
Next node
如果您只想处理类型为“ SCPC”的节点,那么仅是更改queryNodes
列表的情况,如下所示:
Set queryNodes = oXmlFile.SelectNodes("/catalog/query[genre='SCPC']")
访问子节点的代码不会改变,只是因为我们对列表进行了不同的过滤。所有更改都包含在我们创建queryNodes
列表的方式中。可以从组合框的事件处理程序中调用更新queryNodes
的代码,该组合框允许用户选择一种类型。
我们可以将用于打印所有节点值的代码修改为一个子程序,该子程序将打印特定节点的值(如Tim Williams在评论中所建议的那样):
Sub printNode(node As IXMLDOMNode)
Debug.Print "Q: " & node.SelectSingleNode("question").Text & vbCrLf & _
"A: " & node.SelectSingleNode("answer").Text & vbCrLf & _
"C: " & node.SelectSingleNode("comment").Text & vbCrLf & _
"G: " & node.SelectSingleNode("genre").Text & vbCrLf & vbCrLf
End Sub
要控制通过您的界面显示哪个节点,请使用Item
列表的queryNodes
属性。第一个节点是queryNodes.Item(0)
,下一个节点是queryNodes.Item(1)
,依此类推。
如果我们使用名为position
的变量来跟踪列表中的位置,则界面中的“上一个”按钮应为position = position - 1
,而“下一个”按钮应为position = position + 1
。
因此,一旦用户按下“上一个”或“下一个”,我们将更新position
,然后调用printNode queryNodes.Item(position)
。总是有可能我们超出了列表的开头或结尾,可以在尝试调用If Not queryNodes.Item(position) Is Nothing
之前用printNode
进行检查。
对于您的特定情况,您需要一个子项来填充界面中的字段。为此,将printNode
重命名为loadNode
,而不是打印到“调试”窗口,而是将相关的文本从每个子节点复制到界面的相应字段中。
一个saveNode
函数正好相反-将界面中每个字段的值复制到相关子节点的text属性中