25

下面的示例...从解析的 JSON 字符串中循环遍历对象会返回错误“对象不支持此属性或方法”。谁能建议如何使这项工作?非常感谢(在这里提问之前我花了 6 个小时寻找答案)。

将 JSON 字符串解析为对象的函数(这工作正常)。

Function jsonDecode(jsonString As Variant)
    Set sc = CreateObject("ScriptControl"): sc.Language = "JScript" 
    Set jsonDecode = sc.Eval("(" + jsonString + ")")
End Function

遍历解析的对象会返回错误“对象不支持此属性或方法”。

Sub TestJsonParsing()
    Dim arr As Object 'Parse the json array into here
    Dim jsonString As String

    'This works fine
    jsonString = "{'key1':'value1','key2':'value2'}"
    Set arr = jsonDecode(jsonString)
    MsgBox arr.key1 'Works (as long as I know the key name)

    'But this loop doesn't work - what am I doing wrong?
    For Each keyName In arr.keys 'Excel errors out here "Object doesn't support this property or method"
        MsgBox "keyName=" & keyName
        MsgBox "keyValue=" & arr(keyName)
    Next
End Sub 

PS。我已经查看了这些库:

- vba-json无法使示例正常工作。
- VBJSON不包含 vba 脚本(这可能有效,但不知道如何将其加载到 Excel 中,并且文档最少)。

另外,是否可以访问多维解析的 JSON 数组?只是让一个基本的键/值数组循环工作会很棒(对不起,如果要求太多)。谢谢。


编辑:这是使用 vba-json 库的两个工作示例。上面的问题仍然是一个谜......

Sub TestJsonDecode() 'This works, uses vba-json library
    Dim lib As New JSONLib 'Instantiate JSON class object
    Dim jsonParsedObj As Object 'Not needed

    jsonString = "{'key1':'val1','key2':'val2'}"
    Set jsonParsedObj = lib.parse(CStr(jsonString))

    For Each keyName In jsonParsedObj.keys
        MsgBox "Keyname=" & keyName & "//Value=" & jsonParsedObj(keyName)
    Next

    Set jsonParsedObj = Nothing
    Set lib = Nothing
End Sub

Sub TestJsonEncode() 'This works, uses vba-json library
    Dim lib As New JSONLib 'Instantiate JSON class object
    Set arr = CreateObject("Scripting.Dictionary")

    arr("key1") = "val1"
    arr("key2") = "val2"

    MsgBox lib.toString(arr)
End Sub
4

6 回答 6

31

JScriptTypeInfo对象有点不幸:它包含所有相关信息(如您在Watch窗口中所见),但使用 VBA 似乎无法获得它。

如果JScriptTypeInfo实例引用了 Javascript 对象,For Each ... Next则不起作用。但是,如果它引用一个 Javascript 数组,它确实可以工作(参见GetKeys下面的函数)。

所以解决方法是再次使用 Javascript 引擎来获取我们无法使用 VBA 的信息。首先,有一个函数可以获取 Javascript 对象的键。

一旦你知道了密钥,下一个问题就是访问属性。如果密钥的名称仅在运行时已知,VBA 也无济于事。所以有两种方法可以访问对象的属性,一种用于值,另一种用于对象和数组。

Option Explicit

Private ScriptEngine As ScriptControl

Public Sub InitScriptEngine()
    Set ScriptEngine = New ScriptControl
    ScriptEngine.Language = "JScript"
    ScriptEngine.AddCode "function getProperty(jsonObj, propertyName) { return jsonObj[propertyName]; } "
    ScriptEngine.AddCode "function getKeys(jsonObj) { var keys = new Array(); for (var i in jsonObj) { keys.push(i); } return keys; } "
End Sub

Public Function DecodeJsonString(ByVal JsonString As String)
    Set DecodeJsonString = ScriptEngine.Eval("(" + JsonString + ")")
End Function

Public Function GetProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Variant
    GetProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function

Public Function GetObjectProperty(ByVal JsonObject As Object, ByVal propertyName As String) As Object
    Set GetObjectProperty = ScriptEngine.Run("getProperty", JsonObject, propertyName)
End Function

Public Function GetKeys(ByVal JsonObject As Object) As String()
    Dim Length As Integer
    Dim KeysArray() As String
    Dim KeysObject As Object
    Dim Index As Integer
    Dim Key As Variant

    Set KeysObject = ScriptEngine.Run("getKeys", JsonObject)
    Length = GetProperty(KeysObject, "length")
    ReDim KeysArray(Length - 1)
    Index = 0
    For Each Key In KeysObject
        KeysArray(Index) = Key
        Index = Index + 1
    Next
    GetKeys = KeysArray
End Function


Public Sub TestJsonAccess()
    Dim JsonString As String
    Dim JsonObject As Object
    Dim Keys() As String
    Dim Value As Variant
    Dim j As Variant

    InitScriptEngine

    JsonString = "{""key1"": ""val1"", ""key2"": { ""key3"": ""val3"" } }"
    Set JsonObject = DecodeJsonString(CStr(JsonString))
    Keys = GetKeys(JsonObject)

    Value = GetProperty(JsonObject, "key1")
    Set Value = GetObjectProperty(JsonObject, "key2")
End Sub

笔记:

  • 该代码使用早期绑定。因此,您必须添加对“Microsoft Script Control 1.0”的引用。
  • InitScriptEngine在使用其他函数进行一些基本初始化之前,您必须调用一次。
于 2011-09-04T17:34:52.277 回答
7

Codo 的答案很棒,并且构成了解决方案的支柱。

但是,您是否知道 VBA 的CallByName让您在查询 JSON 结构方面取得了很大进展。我刚刚在Google Places Details to Excel with VBA中编写了一个解决方案,例如。

实际上只是重写了它,而没有按照这个例子设法使用添加到 ScriptEngine 的功能。我仅使用 CallByName 实现了遍历数组。

所以一些示例代码来说明

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

Option Explicit

Sub TestJSONParsingWithVBACallByName()

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

    Dim jsonString As String
    jsonString = "{'key1':'value1','key2':'value2'}"

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

    Debug.Assert VBA.CallByName(objJSON, "key1", VbGet) = "value1"
    Debug.Assert VBA.CallByName(objJSON, "key2", VbGet) = "value2"

    Dim jsonStringArray As String
    jsonStringArray = "[ 1234, 4567]"

    Dim objJSONArray As Object
    Set objJSONArray = oScriptEngine.Eval("(" + jsonStringArray + ")")

    Debug.Assert VBA.CallByName(objJSONArray, "length", VbGet) = "2"

    Debug.Assert VBA.CallByName(objJSONArray, "0", VbGet) = "1234"
    Debug.Assert VBA.CallByName(objJSONArray, "1", VbGet) = "4567"


    Stop

End Sub

它还包含子对象(嵌套对象)以及在Google Places Details to Excel with VBA中查看 Google Maps 示例

编辑:不要使用 Eval,尝试更安全地解析 JSON,请参阅此博客文章

于 2016-06-02T19:08:51.083 回答
3

超级简单的答案 - 通过 OO 的力量(或者是 javascript ;)您可以添加您一直想要的 item(n) 方法!

我的完整答案在这里

Private ScriptEngine As ScriptControl

Public Sub InitScriptEngine()
    Set ScriptEngine = New ScriptControl
    ScriptEngine.Language = "JScript"
    ScriptEngine.AddCode "Object.prototype.myitem=function( i ) { return this[i] } ; "
    Set foo = ScriptEngine.Eval("(" + "[ 1234, 2345 ]" + ")") ' JSON array
    Debug.Print foo.myitem(1) ' method case sensitive!
    Set foo = ScriptEngine.Eval("(" + "{ ""key1"":23 , ""key2"":2345 }" + ")") ' JSON key value
    Debug.Print foo.myitem("key1") ' WTF

End Sub
于 2013-10-14T11:21:05.583 回答
2

所以它是 2020 年,但由于缺乏端到端的解决方案,我偶然发现了这个线程。它确实有帮助,但是如果我们需要在运行时动态访问没有 Key 的数据,上面的答案仍然需要进行一些调整才能获得所需的数据。

我终于想出了一个函数,可以为 VBA 中的这个 JSON 解析问题提供端到端的简洁解决方案。这个函数的作用是,它将一个 JSON 字符串(嵌套到任何级别)作为输入并返回一个格式化的二维数组。该数组可以通过普通的 i/j 循环进一步轻松地移动到 Worksheet,或者由于其基于索引的简单可访问性而可以方便地播放。

样本输入输出

该函数保存在我的 Github 存储库的 JSON2Array.bas 文件中。 JSON2Array-VB

.bas 文件中还包含一个演示使用子例程。请下载并导入您的 VBA 模块中的文件。我希望它有所帮助。

于 2020-05-16T19:24:26.160 回答
2

由于 Json 只不过是字符串,因此如果我们能够以正确的方式操作它,无论结构多么复杂,它都可以轻松处理。我认为没有必要使用任何外部库或转换器来做到这一点。这是我使用字符串操作解析 json 数据的示例。

Sub Json_data()
Const URL = "https://api.redmart.com/v1.5.8/catalog/search?extent=2&pageSize=6&sort=1&category=bakery"
Dim http As New XMLHTTP60, html As New HTMLDocument
Dim str As Variant

With http
    .Open "GET", URL, False
    .send
    str = Split(.responseText, "category_tags"":")
End With
On Error Resume Next
y = UBound(str)

    For i = 1 To y
        Cells(i, 1) = Split(Split(str(i), "title"":""")(1), """")(0)
        Cells(i, 2) = Split(Split(str(i), "sku"":""")(1), """")(0)
        Cells(i, 3) = Split(Split(str(i), "price"":")(1), ",")(0)
        Cells(i, 4) = Split(Split(str(i), "desc"":""")(1), """")(0)
    Next i
End Sub
于 2017-06-16T19:15:09.437 回答
0

我知道已经很晚了,但是对于那些不知道如何使用VBJSON的人,你只需要:

1) 将 JSON.bas 导入您的项目(打开 VBA 编辑器,Alt + F11;文件 > 导入文件)

2) 添加字典引用/类 对于仅限 Windows,包括对“Microsoft Scripting Runtime”的引用

您也可以以相同的方式使用VBA-JSON,它特定于 VBA 而不是 VB6,并且具有所有文档。

于 2017-03-22T19:17:01.297 回答