如何将DOMDocument传递给子过程?XJustiz XML解析报错排查
问题详情
我有一批德国商事登记处的结构化XML数据(单文件对应单公司),符合XJustiz XML Schema(司法领域电子数据交换规范),当前数据同时存在v1和v3两个版本,Schema会定期更新结构、ID和标签名。
已编写独立代码处理不同版本的XML并导出数据到工作簿,现在要整合代码:主循环遍历指定目录的XML文件,加载为MSXML2.DOMDocument60实例,判断XJustiz版本后调用对应子过程。但子过程中访问DOM节点时始终触发Run-Time Error '91':对象变量或With块变量未设置,确认XML存在目标节点,且xmlDoc.Text能获取内容,但无法访问XML结构,无论传递DOM实例还是文件路径重新加载都无效,希望保留分模块的代码结构,寻求解决方案。
主过程代码
Sub Read_XML_Data() [...] '########## MAIN LOOP START ########## Do While Len(strFilePath) > 0 Set xmlDoc = New MSXML2.DOMDocument60 xmlDoc.async = False xmlDoc.Load (strFolder & strFilePath) 'Checking the XJustiz version For Each xmlNode In xmlDoc.getElementsByTagName("*") If StrComp(xmlNode.Attributes(0).Name, "xjustizversion", vbTextCompare) = 0 Then Select Case Left(xmlNode.Attributes(0).Text, 1) 'Found version 1 and execute corresponding subroutine Case 1 Read_XJustiz_v1 xmlDoc Exit For Case Else MsgBox (strFilePath & " verwendet XJustiz Version " & Left(xmlNode.Attributes(0).Text, 1)) Exit For End Select End If Next strFilePath = Dir Loop '########## MAIN LOOP END ########## End Sub
子过程代码
Sub Read_XJustiz_v1(ByRef xmlDoc As DOMDocument60) Dim strContent As String strContent = xmlDoc.Text 'This line raises the Error No. 91: Object Variable or With Block Variable Not Set. If xmlDoc.getElementsByTagName("Beteiligter").Item(0).ChildNodes(1).ChildNodes(2).Text = "Gesellschaft mit beschränkter Haftung" Then [...] End If End Sub
解决方案
1. 先确认XML加载是否成功
主过程中加载XML后,先检查加载状态,避免传递未成功加载的DOM:
Set xmlDoc = New MSXML2.DOMDocument60 xmlDoc.async = False xmlDoc.validateOnParse = False '不需要验证Schema可关闭 If Not xmlDoc.Load(strFolder & strFilePath) Then MsgBox "文件加载失败: " & strFilePath & vbCrLf & xmlDoc.parseError.Reason GoTo NextFile '跳过该文件 End If
2. 处理命名空间问题(核心原因)
XJustiz XML通常带有命名空间,getElementsByTagName默认不识别命名空间,导致找不到节点。改用带命名空间的XPath查询:
在子过程中添加命名空间管理器:
Sub Read_XJustiz_v1(ByRef xmlDoc As MSXML2.DOMDocument60) Dim strContent As String Dim nsMgr As MSXML2.XMLNamespaceManager Dim beteiligterNodes As MSXML2.IXMLDOMNodeList Dim targetNode As MSXML2.IXMLDOMNode strContent = xmlDoc.Text '初始化命名空间管理器,替换为XML实际的命名空间URI Set nsMgr = New MSXML2.XMLNamespaceManager(xmlDoc.documentElement) nsMgr.AddNamespace "xj", "http://www.xjustiz.de/namespace/xjustiz/1.0" 'XJustiz v1的标准命名空间 '用XPath查询目标节点 Set beteiligterNodes = xmlDoc.selectNodes("//xj:Beteiligter", nsMgr) If beteiligterNodes.Length > 0 Then '按节点名查询子节点,避免依赖ChildNodes索引(易受空白节点影响) Set targetNode = beteiligterNodes(0).selectSingleNode("xj:Unternehmensform/xj:Bezeichnung", nsMgr) '替换为实际节点路径 If Not targetNode Is Nothing Then If targetNode.Text = "Gesellschaft mit beschränkter Haftung" Then '执行后续逻辑 End If End If End If End Sub
3. 避免依赖ChildNodes索引
XML中的换行、空格会被解析为空白文本节点,导致ChildNodes(1)、ChildNodes(2)的索引与预期不符。改用selectSingleNode按节点名定位,确保精准找到目标节点。
4. 优化版本判断逻辑
原代码遍历所有节点并直接访问Attributes(0),若节点无属性会触发错误。优化为优先检查根节点属性,再遍历其他节点:
'Checking the XJustiz version Dim versionAttr As MSXML2.IXMLDOMAttribute '优先检查根节点的xjustizversion属性 Set versionAttr = xmlDoc.documentElement.Attributes.getNamedItem("xjustizversion") If Not versionAttr Is Nothing Then Select Case Left(versionAttr.Value, 1) Case "1" Read_XJustiz_v1 xmlDoc Case "3" Read_XJustiz_v3 xmlDoc '对应v3子过程 Case Else MsgBox strFilePath & " 使用XJustiz版本: " & versionAttr.Value End Select Else '遍历所有节点查找属性 For Each xmlNode In xmlDoc.getElementsByTagName("*") Set versionAttr = xmlNode.Attributes.getNamedItem("xjustizversion") If Not versionAttr Is Nothing Then Select Case Left(versionAttr.Value, 1) Case "1" Read_XJustiz_v1 xmlDoc Case "3" Read_XJustiz_v3 xmlDoc Case Else MsgBox strFilePath & " 使用XJustiz版本: " & versionAttr.Value End Select Exit For End If Next End If
5. 确认参数类型与库引用
- 子过程参数需明确指定
MSXML2.DOMDocument60,避免类型不匹配:Sub Read_XJustiz_v1(ByRef xmlDoc As MSXML2.DOMDocument60) - 确保VBA工程已引用
Microsoft XML, v6.0(菜单:工具 -> 引用)
内容的提问来源于stack exchange,提问作者LuPi1801
相关产品推荐
相关产品推荐

