【问题标题】:Quickest Way to Parse XML解析 XML 的最快方法
【发布时间】:2020-08-27 14:40:19
【问题描述】:

我有巨大的 XML 文件,每个大约 1GB。它们太大了,由于数据量大,即使在 Notepad++ 中也无法打开。

我编辑了 XML 并能够通过 DOMDocument60 对其进行解析(感谢 stackflow 提供的帮助)。

我在Improve speed of VBA 阅读了类似的问题,但我仍然无法正确实施,因此需要一些指导。

例如:

  1. 如何在 SAX 中加载 xml?在 SAX 中读取之前,我是否需要将其加载到 DOMDocument60 中?
  2. 导入后如何在 SAX 中逐行读取?在 DOMDocument60 中,我可以轻松跳转到任何节点,然后循环遍历子节点,但不确定如何在 SAX 中执行此操作?
  3. 与 DOMDocument 相比,SAX 解析大型 XML 的时间差是多少?我还没有找到任何关于此的实时示例。
  4. 在 VBA 中是否有比 SAX 更好的选择,就像我可以用来加快解析过程的任何其他库一样。

感谢您的建议。 (示例 XML 文件如下)

<ParentNode type="actual">

<SampleObject class="POC" version="XYZ123" distName="Test1" id="Sample">
  <p name="name">POC1</p>
  <p name="object1">0</p>
  <p name="object2">6</p>
  <p name="object3">0</p>
</SampleObject>

<SampleObject class="POC" version="XYZ123" distName="Test2" id="Sample">
  <p name="name">POC1</p>
  <p name="object1">2</p>
  <p name="object2">10</p>
  <p name="object4">4</p>
  <p name="object3">6</p>
</SampleObject>

<SampleObject class="POC" version="XYZ123" distName="Test3" id="Sample">
  <p name="name">POC1</p>
  <p name="object2">90</p>
  <p name="object3">0</p>
</SampleObject>

<SampleObject class="POC" version="XYZ123" distName="Test4" id="Sample">
  <p name="name">POC1</p>
  <p name="object1">2</p>
  <p name="object2">10</p>
  <p name="object4">40</p>
  <p name="object3">61</p>
</SampleObject>

【问题讨论】:

  • SAX 的重点是您不会一次性加载整个文档 - 您可以“随时”解析它。它比典型的基于 DOM 的解析稍微复杂一些,但有一些示例(VB6 示例应该可以转换为 VBA)
  • developerfusion.com/article/84405/sax-and-vb-6 - 这适用于 VB6,但似乎适用于 VBA。我以前没有使用过 SAX,但我使用您的示例 XML 在 5 分钟内启动并运行
  • 非常感谢蒂姆。我在网上查看了一些示例,在 [link](docs.microsoft.com/en-us/previous-versions/windows/desktop/…) 中提到了使用 SAX 的 Microsoft 示例之一。这个例子让我很困惑,我可能可以将 DOM 加载方法与 sax 一起使用。我会看看你提到的例子,看看我能不能让它工作。
  • SAX 是否真正具有优势取决于您需要对 XML 内容做什么:如果您只是需要将内容提取为其他格式或只读部分内容,那么 SAX 可能会起作用。
  • 目的是解析整个1GB大小、1000条记录的XML文件。 DOM 进程可以工作,但速度很慢,需要数小时才能解析它。我认为如果我逐行阅读 SAX 会起作用,只是第一次尝试使用它。

标签: xml vba ms-access xml-parsing


【解决方案1】:

这里是我按照上面发布的链接并使用您的示例 XML 的地方。 只是输出到即时窗口:我不知道你在用提取的数据做什么......

常规模块中的测试方法:

Sub Tester()

    Const FNAME As String = "example.xml"
    Dim rdr As New MSXML2.SAXXMLReader30
    Dim cnth As New ContentHandler

    Set rdr.ContentHandler = cnth
    rdr.parseURL ThisWorkbook.Path & "\" & FNAME  'test xml file is in same folder as the workbook

End Sub

类模块ContentHandler:

Option Explicit

Implements IVBSAXContentHandler

Dim cls, vers, distName, id, pName, pContent
Dim inSO As Boolean, inP As Boolean

Private Sub IVBSAXContentHandler_characters(strChars As String)
    If inP Then Debug.Print "P content:", strChars
End Sub

Private Sub IVBSAXContentHandler_startElement(strNamespaceURI As String, _
                             strLocalName As String, strQName As String, _
                             ByVal oAttributes As MSXML2.IVBSAXAttributes)
    Select Case strLocalName
        Case "SampleObject"
            inSO = True
            cls = oAttributes.getValueFromName("", "class")
            vers = oAttributes.getValueFromName("", "version")
            distName = oAttributes.getValueFromName("", "distName")
            id = oAttributes.getValueFromName("", "id")
            Debug.Print "Start", strLocalName, cls, vers, distName, id
        Case "p"
            inP = True
            pName = oAttributes.getValueFromName("", "name")
            Debug.Print "Start", strLocalName, pName
    End Select
End Sub

Private Sub IVBSAXContentHandler_endElement(strNamespaceURI As String, strLocalName As String, strQName As String)
    Select Case strLocalName
        Case "SampleObject"
            inSO = False
            cls = ""
            vers = ""
            distName = ""
            id = ""
        Case "p"
            pName = ""
            inP = False
    End Select
End Sub

Private Property Set IVBSAXContentHandler_documentLocator( _
                            ByVal RHS As MSXML2.IVBSAXLocator)
End Property

Private Sub IVBSAXContentHandler_startDocument()
End Sub

Private Sub IVBSAXContentHandler_endDocument()
End Sub

Private Sub IVBSAXContentHandler_endPrefixMapping(strPrefix As String)
End Sub

Private Sub IVBSAXContentHandler_ignorableWhitespace(strChars As String)
End Sub

Private Sub IVBSAXContentHandler_processingInstruction(strTarget As String, strData As String)
End Sub

Private Sub IVBSAXContentHandler_skippedEntity(strName As String)
End Sub

Private Sub IVBSAXContentHandler_startPrefixMapping(strPrefix As String, strURI As String)
End Sub

【讨论】:

  • 非常感谢蒂姆,我认为您为 excel 定义了代码,但我会这样做以供访问。希望它会以同样的方式工作。曾经,我能够解析 xml,然后我想将其存储在 Access 表中。属性 class="POC" 将是表名,因此我将在开始时创建它。正如我之前所说,我试图弄清楚如果我使用 SAX 与 DOM 相比它会产生什么时差。我相信它会快得多。我还在 vb.net 中使用 linq 查询完成了解析,这似乎运行良好,但随后我将远离 Office 平台 (vba)。
  • 如果我成功了,我会给出反馈。如果没有,我会报告任何错误,非常感谢
  • 谢谢伙计,访问权限似乎工作得很好,我现在必须完成不同类型对象的代码,然后将值存储在访问表中。它似乎完美地逐行阅读
  • 嗨,伙计,这段代码几乎没有问题 - 首先我尝试使用 SAXXMLReader60 而不是 SAXXMLReader30,它在 rdr.parseURL 处出现错误。如果我尝试使用您提到的 SAXXMLReader30,那么它在读取该行时可以工作,但是如果我尝试在 select 语句(IVBSAXContentHandler_startElement)中调用任何 Sub,那么它会产生错误。错误代码再次与 parseURL 一起显示。
【解决方案2】:

我尝试在“IVBSAXContentHandler_StartElement”中输入一个新的 Sub(在每种选择情况下),但它会产生错误。

Public Sub Ins_2G1(strLocalName As String, cls As String, vers As String, distName As String, id As String)
    Dim DNameArr() As String
    Dim insertcol As String
    Dim insertval As String

    DNameArr() = Split(distName, "/")
    colvalues(0) = distName
    colvalues(1) = DNameArr(1)
    colvalues(2) = DNameArr(2)
    colvalues(3) = DNameArr(3)

    'Converting Generated Parameter Name Array in to String
    insertcol = ""
    For i = LBound(colnames) To UBound(colnames)
        insertcol = insertcol + CStr(colnames(i))
        If i < UBound(colnames) Then
            insertcol = insertcol + ","
        End If
    Next

    'Converting Generated Value Array in to String
    insertval = "'"
    For i = LBound(colvalues) To UBound(colvalues) + 3
        insertval = insertval + CStr(colvalues(i))
        If i < UBound(colvalues) Then
            insertval = insertval + "','"
        End If
    Next
    insertval = insertval + "'"

    'Inserting Record in to POC table
    strSql = "INSERT INTO [" & cls & "] (" & insertcol & ") VALUES (" & insertval & ");"
    db.Execute strSql


End Sub

【讨论】:

  • “它会生成一个错误”对于运行代码时发生的确切情况的描述不是很有用...如果您有后续问题,那么最好创建一个新帖子,包括所有相关代码和错误详细信息等。
  • 抱歉,伙计,我很想提出新问题,但它会产生一个错误,我已达到限制,所以我无法提出新问题。这就是我将代码放在这里的原因。当我说它产生错误时,它没有给出错误的任何描述。它只是说“系统错误-2146828283”我认为这会产生一个错误,因为其中一行中的结构从

    更改为 并带有一些子项。我不知道我是否可以跳过任何一行。上面的代码显示我试图将变量传递给另一个子,然后将其插入数据库

  • 可能值得解析整个文档,只需计算您拥有的每种不同元素类型的数量,甚至无需尝试提取数据。如果您的 XML 不遵守特定结构,那么创建代码来管理特定结构是没有意义的。
  • 同意蒂姆。我从 DOM 的角度思考,我可以跳转到特定的节点,然后相应地解析子节点。在 SAX 中,代码的工作方式不同,如果行结构发生更改,则会生成错误,因此 SAX 存在限制,即结构需要保持不变。不幸的是,源文件不在我的控制之下,它可以有不同的结构。这就是我使用“IF”条件的原因。我想跳过任何不以

    开头的行。所以你定义的两种情况正是我需要的

  • 另外,我看到有两个处理程序 IVBSAXContentHandler_processingInstruction 和 IVBSAXContentHandler_skippedEntity 但如果我必须跳过除

    以外的任何条目,我不确定如何使用它们
猜你喜欢
  • 2013-09-01
  • 1970-01-01
  • 2011-07-05
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 2018-01-17
  • 2015-01-23
  • 2010-10-11
相关资源
最近更新 更多