VBA - 使用递归从 JSON 创建 MSAccess 关系表

Posted

技术标签:

【中文标题】VBA - 使用递归从 JSON 创建 MSAccess 关系表【英文标题】:VBA - Create MSAccess relational tables from JSON using recursion 【发布时间】:2021-06-14 23:09:31 【问题描述】:

在使用各种不同的方法解决这个问题几天后,我终于意识到我可能已经咬得比我能咀嚼的多。我有一个结构为字典集合的 JSON 提取。这是一个相当复杂的结构,但每个字典在整个 JSON 中都是一致的。我正在尝试做的是递归遍历 JSON,构建和链接表。我已经接近了,但我的递归遇到了一些问题。为简单起见,所有字段都是字符串。

JSON 在字典集合中的结构如下:

 Dictionary 1.0
      key/value pair 1a
      key/value pair 1b
      key/value pair 1c
      subDictionary 1.1
          key/value pair 1.1a
          key/value pair 1.1b
          key/value pair 1.1c
     subDictionary 1.2
          key/value pair 1.2a
          key/value pair 1.2b
          key/value pair 1.2c

 Dictionary 2.0
      key/value pair 2a
      key/value pair 2b
      key/value pair 2c
      subDictionary 2.1
          key/value pair 2.1a
          key/value pair 2.1b
          key/value pair 2.1c
     subDictionary 2.2
          key/value pair 2.2a
          key/value pair 2.2b
          key/value pair 2.2c

我最终想要的是一个父/子关系数据库,其中包含两个(或可能更多)由 ID 链接的表:

 Parent Table
 ID     Field 1a     Field 1b     Field 1c 
 1.0    Value 1a      Value 1b     Value 1c
 2.0    Value 2a      Value 2b     Value 2c

 Sub Table
 parentID  ID     key1.1a        key1.1b       key1.1c
 1.0       1.1    Value 1.1a     Value 1.1b    Value 1.1c
 1.0       1.2    Value 1.2a     Value 1.2b    Value 1.2c
 2.0       2.1    Value 2.1a     Value 2.1b    Value 2.1c
 2.0       2.2    Value 2.2a     Value 2.2b    Value 2.2c

到目前为止,我的代码可以进行递归,但我还需要它来构建 SQL 语句来定义表和填充数据。我知道这是一个很大的问题,但我认为很多人会在这段代码中发现将 JSON 导入 MS Access 关系表的价值。我目前正在将数据加载到 2D 数组中,但我的下一步是构建多个 2D 数组(每个表一个)或直接构建 SQL 语句以构建新表并将其链接到数据元素,并且完全忘记构建数组。

我从我的主程序中获取 JSON 字符串并对其进行解析:

  sub Main
       Dim strResult As Variant

       Set strResult = JsonConverter.ParseJson([add your json string here])
       ReDim BuildArray(strResult.Count, 1) As String

       ColNum = 0
       RowNum = 1        'Row 0 is used to hold the field names

       Call TraverseDictionary(strResult, RowNum, ColNum, BuildArray)

  end sub



 Private Sub TraverseDictionary(myDictionary As Variant, RowNum As Integer, ColNum As Integer, BuildArray As Variant)
    Dim key As Variant
    Dim myObject As Variant
    Dim myElement As String

    Select Case TypeName(myDictionary)

    Case "Dictionary"

        For Each key In myDictionary.Keys
        
            If TypeName(key) <> "Dictionary" And TypeName(key) <> "Collection" Then
                
                BuildArray(0, ColNum) = Trim(key)
                ColNum = ColNum + 1
                If ColNum > UBound(BuildArray, 2) Then
                    ReDim Preserve BuildArray(UBound(BuildArray, 1), ColNum)
                End If
            Else
                Debug.Print "Table = ", myDictionary.Keys(1)
                ArrFieldType.Add TypeName(key) & " - Case Dictionary"
            End If
            
            TraverseDictionary myDictionary(key), RowNum, ColNum, BuildArray
            
        Next key

    Case "Collection"
    
        For Each key In myDictionary
        
            If TypeName(key) <> "Dictionary" And TypeName(key) <> "Collection" Then
                BuildArray(0, ColNum) = Trim(key)
                ColNum = ColNum + 1
                If ColNum > UBound(BuildArray, 2) Then
                    ReDim Preserve BuildArray(UBound(BuildArray, 1), ColNum)
                End If
            Else
                
                myElement = CStr(key(1))
                ArrFieldType.Add TypeName(key) & " - Case Collection"
                
            End If
            
            TraverseDictionary key, RowNum, ColNum, BuildArray, ArrFieldType
        
        Next
        
        If ColNum > 0 Then
            RowNum = RowNum + 1
            ColNum = 0
        End If
    
       Case Else
                
                If IsNull(myDictionary) Then
                    myElement = ""
                Else
                    myElement = CStr(myDictionary)
                End If
                
                BuildArray(RowNum, ColNum) = myElement
                
     End Select


 End Sub

有人愿意提供一些指导吗?提前致谢。一旦我让递归工作,我想实现

中描述的方法
 https://***.com/questions/62437764/importing-json-file-to-ms-access-table

创建将构建和填充表的 SQL 语句,并创建索引。

结果将是一个小函数,它将接受几乎任何 JSON 片段并将其转换为 MS Access 链接表。

【问题讨论】:

【参考方案1】:

我没有使用字典,而是集合,因为它们是 Access 原生的。

这种方式递归拆分接收到的Json相对容易。一个例子是模块 JsonCollection 在这里找到:

VBA.CVRAPI

诀窍是检查节点持有什么;如果它是一个集合,则更深一层。

要创建表、索引和关系,您应该使用DAO。它比你最终得到的 SQL 和 ALTER 表和字段的混乱要干净得多。一个例子是模块 WtziData 在这里找到:

VBA.Timezone-Windows

【讨论】:

感谢您的反馈! :-) 我做了一些测试,发现我收到的 JSON 字符串被格式化为字典集合,其中一些元素是嵌套字典。我同意使用 DAO。这个问题变得复杂的地方是我是否应该逐步遍历整个 json 以确定结构(构建数组导致由于 redim 导致的性能问题,然后将数组转换为表)还是在逐步执行时动态构建(构建表,添加列,并通过 SQL 创建关系)。这两种方法各有利弊。 认为我会做两步,首先浏览整个结构 - 至少这会在早期发现错误。

以上是关于VBA - 使用递归从 JSON 创建 MSAccess 关系表的主要内容,如果未能解决你的问题,请参考以下文章

在 Excel VBA 中解析 JSON

在反应中使用递归函数从平面数组创建 JSX 树

VBA如何在使用许多整数时避免递归

使用 jq 从 JSON 文件中递归获取键名

递归 JSON 模式

使用 VBA 从 MS Access 表创建格式化文本文件