json - 在Windows Excel VBA中,如何获取JSON key 来抢占 "Run-time error ' 43 8': Object doesn' t支持此属性或方法”?

标签 json excel vba

在这里回答我自己的问题。
我已经在 Excel VBA 中使用 JSON 做了一些工作,并发布了很多发现,我将以问答的形式进行 https://stackoverflow.com/help/self-answer https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

因此,在 stackoverflow 的其他地方,人们可以看到有关在 VBA 中解析 JSON 的问题,但他们似乎错过了一两个技巧。

首先,我不再使用自定义 JSON 解析库,而是使用 ScriptControl 的 Eval 方法作为我所有 JSON 代码的基础。 我们还表达了对 native Microsoft 解决方案的偏好。

这是一个先前的问题In Excel VBA on Windows, how to mitigate issue of dot syntax traversal of parsed JSON broken by IDE's capitalisation behaviour?这个问题就是以此为基础的。它展示了如何使用 VBA.CallByName 更加稳健 而不是使用点语法来遍历已解析的 JSON 对象。还有另一个先前的问题In Excel VBA on Windows, how to loop through a JSON array parsed?展示了它如何也可以 用于访问数组元素。但是 CallByName 返回一个奇怪的变量类型,该类型在“监视”窗口中显示为 Object/JScriptTypeInfo 如果在立即窗口中键入 Debug.Print(或将鼠标悬停在变量上),则会得到无信息的“[object Object]”。在另一个问题中 系列中In Excel VBA on Windows, how to get stringified JSON respresentation instead of “[object Object]” for parsed JSON variables?我提出了一些调试“糖”,可以很好地检查变量。

在这个问题中,我问如何以编程方式获取成员列表,通过该列表我可以检测 key 的存在,这将有助于 预防任何“运行时错误'438':对象不支持此属性或方法”错误并允许我们编写防御性(希望是“防弹”)代码?

这是 5 个系列的第 4 个问题。这是完整的系列

第一季度 In Excel VBA on Windows, how to mitigate issue of dot syntax traversal of parsed JSON broken by IDE's capitalisation behaviour?

第二季度 In Excel VBA on Windows, how to loop through a JSON array parsed?

第三季度 In Excel VBA on Windows, how to get stringified JSON respresentation instead of “[object Object]” for parsed JSON variables?

第四季度 In Windows Excel VBA,how to get JSON keys to pre-empt “Run-time error '438': Object doesn't support this property or method”?

第五季度 In Excel VBA on Windows, for parsed JSON variables what is this JScriptTypeInfo anyway?

最佳答案

与使用解析的 JSON 对象相关的其他堆栈溢出问题的答案使用迷你脚本方法,我们可以在此处使用此方法。 如果我们说我们正在运行 Microsoft Windows 版本的 Excel VBA,那么我们可以使用 Microsoft Scripting Runtime 库中的脚本字典。

我们可以在 Javascript 中创建 Scripting.Dictionary,用 JSON 对象的键填充它,并使用这些值作为子元素的引用, 最后传回VBA。在 VBA 中,可以使用字典的 Exists 方法来防止丢失键。可以使用Dictionary的Count方法 确定其他下游变量的尺寸。人们甚至可以使用 Dictionary 的 Item 方法来检索子元素(仅向下一级)。

因此,

'Tools->References->
'Microsoft Scripting Runtime
'Microsoft Script Control 1.0;  {0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}; C:\Windows\SysWOW64\msscript.ocx

Option Explicit

Private Function GetScriptEngine() As ScriptControl
    Static soScriptEngine As ScriptControl
    If soScriptEngine Is Nothing Then
        Set soScriptEngine = New ScriptControl
        soScriptEngine.Language = "JScript"

        soScriptEngine.AddCode "function getKeyValues(jsonObj) { " & _
                              " var dictionary = new ActiveXObject(""Scripting.Dictionary""); " & _
                              " var keys = new Array(); for (var i in jsonObj) { dictionary.add(i,jsonObj[i]); }; return dictionary; } "


    End If
    Set GetScriptEngine = soScriptEngine
End Function


Private Sub TestJSONParsingWithCallByName3()

    Dim oScriptEngine As ScriptControl
    Set oScriptEngine = GetScriptEngine

    Dim sJsonString As String
    sJsonString = "{'key1': 'value1'  ,'key2': { 'key3': 'value3' } }"

    Dim objJSON As Object
    Set objJSON = oScriptEngine.Eval("(" + sJsonString + ")")

    Dim dicKeys As Scripting.Dictionary
    Set dicKeys = oScriptEngine.Run("getKeyValues", objJSON)

    Debug.Assert dicKeys.Count = 2

    Debug.Assert TypeName(dicKeys.Item(dicKeys.Keys()(1))) = "JScriptTypeInfo"
    Stop

    If dicKeys.Exists("foobarbaz") Then

        '*** Next line WOULD throw "Run-time error '438': Object doesn't support this property or method" because "foobarbaz" is not a key
        '*** but is skipped because of defensive code.
        Debug.Assert VBA.CallByName(objJSON, "foobarbaz", VbGet)

    End If

End Sub

但是,我还发现了一个很棒的替代方案,不需要 miniscript 或 Scripting.Dictionary。它将允许抢先丢失 key ,但是 没有集合类功能。它使用了一个鲜为人知的属性 hasOwnProperty()。因此,

'Tools->References->
'Microsoft Script Control 1.0;  {0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}; C:\Windows\SysWOW64\msscript.ocx

Option Explicit

Private Sub TestJSONParsingWithCallByName4()

    Dim oScriptEngine As ScriptControl
    Set oScriptEngine = New ScriptControl
    oScriptEngine.Language = "JScript"

    Dim sJsonString As String
    sJsonString = "{'key1': 'value1'  ,'key2': { 'key3': 'value3' } }"

    Dim objJSON As Object
    Set objJSON = oScriptEngine.Eval("(" + sJsonString + ")")

    Debug.Assert objJSON.hasOwnProperty("key1")
    Debug.Assert objJSON.hasOwnProperty("key2")

    Dim objKey2 As Object
    Set objKey2 = VBA.CallByName(objJSON, "key2", VbGet)

    Debug.Assert objKey2.hasOwnProperty("key3")


    If objJSON.hasOwnProperty("foobarbaz") Then

        '*** Next line WOULD throw "Run-time error '438': Object doesn't support this property or method" because "foobarbaz" is not a key
        '*** but is skipped because of defensive code.
        Debug.Assert VBA.CallByName(objJSON, "foobarbaz", VbGet)

    End If

End Sub

关于json - 在Windows Excel VBA中,如何获取JSON key 来抢占 "Run-time error ' 43 8': Object doesn' t支持此属性或方法”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37711100/

相关文章:

java - 从用脚本编写的网页将数据导入到 Excel

vba - VBA循环宏不显示任何错误,但启动后不显示任何结果

java - 如何解决java.lang.String类型的org.json.JSONException : Value <! DOCTYPE无法转换为JSONObject

java - Android 解码 PHP 文件编码的 JSON

javascript - 如何正确使用Json

performance - 如何优化打开和关闭Excel工作簿以提取数据以加快运行速度

json - 在 R 中从 JSON 转换为数据帧

java - 使用 java 将 xml 中的 Null 值表示为空白

excel - 如何复制数据并直接粘贴到其右侧而不删除所需粘贴范围内已存在的数据?

VBA集合可以定义为静态类型吗?