【问题标题】:Using VBA and VBA-JSON to access JSON data from Wordpress API使用 VBA 和 VBA-JSON 从 Wordpress API 访问 JSON 数据
【发布时间】:2018-12-10 01:17:06
【问题描述】:

我正在构建一个 VBA 应用程序,该应用程序使用从网络上抓取的资源来创建和修改 Wordpress 网站页面。 Wordpress API 返回一个 JSON 文件,但不支持在 VBA 中解析 JSON,因此我从 GitHub 导入了 VBA-JSON。这是子程序:

Sub Wordpress()

    '
    ' Wordpress API Test
    '
    Dim wpResp As Variant
    Dim sourceSheet As String
    Dim resourceURL As String
    sourceSheet = "Resources"
    resourceURL = Sheets(sourceSheet).Cells(6, 1)
    wpResp = getJSON(resourceURL + "/wp-json/wp/v2/posts")

End Sub

以及它调用的函数。

Function getJSON(link) As Object

    Dim response As String
    Dim json As Object
    On Error GoTo recovery
    Dim retryCount As Integer
    retryCount = 0
    Dim web As MSXML2.XMLHTTP60
    Set web = New MSXML2.XMLHTTP60

the_start:

    web.Open "GET", link, False, UserName, pw
    web.setRequestHeader "Content-type", "application/json"
    web.send
    response = web.responseText
    While web.readyState <> 4
        DoEvents
    Wend

    On Error GoTo 0

    Debug.Print link
    Debug.Print web.Status; "XMLHTTP status "; web.statusText; " at "; Time

    Set json = JsonConverter.ParseJson(response)

    'getJSON = json ' this line produces Object variable or With block variable not set error but I can deal with it later

    Exit Function

recovery:

    retryCount = retryCount + 1
    Debug.Print "Error number: " & Err.Number & " " & Err.Description & " Retry " & retryCount
    Application.StatusBar = "Error number: " & Err.Number & " " & Err.Description & " Retry " & retryCount
    If retryCount < 4 Then GoTo the_start Else Exit Function
End Function

此代码返回一个包含 1 个项目的对象/集合,其中包含一个包含 24 个项目的变体/对象/字典,但我不知道如何访问这些项目。截图如下:

如果我使用即时窗口查询 ?json.count 我会得到正确的结果“1”,但在网络上研究了大约六个小时并尝试了尽可能多的变体后,我仍然不知道如何访问其他 24 个。

这是 JSON:

[{"id":1,"date":"2018-06-22T18:13:00","date_gmt":"2018-06-22T22:13:00","guid":{"rendered":"http:\/\/mytestsite.org\/?p=1"},"modified":"2018-06-22T18:13:00","modified_gmt":"2018-06-22T22:13:00","slug":"hello-world","status":"publish","type":"post","link":"http:\/\/mytestsite.org\/hello-world\/","title":{"rendered":"Blog Post Title"},"content":{"rendered":"<p>What goes into a blog post? Helpful, industry-specific content that: 1) gives readers a useful takeaway, and 2) shows you&#8217;re an industry expert. <\/p>\n<p>Use your company&#8217;s blog posts to opine on current industry topics, humanize your company, and show how your products and services can help people.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What goes into a blog post? Helpful, industry-specific content that: 1) gives readers a useful takeaway, and 2) shows you&#8217;re&hellip;<\/p>\n","protected":false},"author":1,"featured_media":212,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/posts\/1"}],"collection":[{"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/comments?post=1"}],"version-history":[{"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/posts\/1\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/media\/212"}],"wp:attachment":[{"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/media?parent=1"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/categories?post=1"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/mytestsite.org\/wp-json\/wp\/v2\/tags?post=1"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}]

归根结底,我希望能够从多个互联网资源中提取和整理数百页的 WP 内容,并使用此应用程序使它们保持最新状态。只要我们不脱离 VBA,这里的问题之外的进一步建议也会很有用。

【问题讨论】:

  • 请分享一个有效的 URL 或发布 JSON 示例。
  • 也许我遗漏了一些东西,但请在即时窗口中尝试?JSON.items(1).items(1),或者如果您提前知道字典的键名?JSON("PutCollectionKeyNameHere")("PutDictionaryKeyNameHere")
  • 另外,在您的退出函数上方,我认为getJSON = JSON 应该是Set getJSON = json,因为您正在分配一个对象。
  • ?json(1).count
  • 这里是 JSON responseText

标签: json wordpress vba api wordpress-rest-api


【解决方案1】:

JsonConverter 正在返回 VBA.Collections Scripting.Dictionaries 和值的集合。为了理解输出,您必须测试所有返回值的TypeName

真正的问题是“如何浏览json 对象(或任何未知对象)并访问其中的值。

立即窗口

使用 OP 帖子中的 Immediate Windowjson 对象,我将尝试描述思考过程(以必读书籍的风格:The Little Schemer

' What is json?
?TypeName(JSON)
Collection

'json is a collection
'How big is JSON
?JSON.Count
 1 

'JSON is a collection of 1 Item
'What is Type that Item?
?TypeName(JSON(1))
Dictionary

'JSON(1) is a Dictionary
'What is the first key in the JSON(1) Dictionary?
?JSON(1).Keys()(0)
id

'The first key in the JSON(1) Dictionary is "id"
'What is the Type of the value of "id"?
?TypeName(JSON(1)("id"))
Double

'JSON(1)("id") is a number
'What is its value
?JSON(1)("id")
 1 

当然,考虑到这个 JSON 对象中的嵌套量,这个过程可能会变得乏味。

JSON(1)("_links")("居里")(1)("模板化")

集合|字典|字典|集合|布尔值

所以我想最好的办法是编写一个函数,将所有访问器打印到Immediate Window 并从那里开始。

PrintJSONAccessors:Sub

Sub PrintJSONAccessors(JSON As Variant, Optional Prefix As String)
    Dim data As Variant, Key As Variant, Value As Variant
    Dim Accessor As String, ArrayAccessor As String
    Dim n As Long
    If TypeName(JSON) = "Collection" Then
        For n = 1 To JSON.Count
            Accessor = Prefix & "(" & n & ")"
            If TypeName(JSON(n)) = "Dictionary" Or TypeName(JSON(n)) = "Collection" Then
                PrintJSONAccessors JSON(n), Accessor
            Else
                Debug.Print Accessor
            End If
        Next
    Else
        For Each Key In JSON
            If TypeName(Key) = "Dictionary" Or TypeName(Key) = "Collection" Then
                PrintJSONAccessors Key, Prefix
            ElseIf TypeName(JSON(Key)) = "Dictionary" Or TypeName(JSON(Key)) = "Collection" Then
                Accessor = Prefix & "(" & Chr(34) & Key & Chr(34) & ")"
                PrintJSONAccessors JSON(Key), Accessor
            ElseIf TypeName(JSON(Key)) = "Dictionary" Then
                Accessor = Prefix & "(" & Chr(34) & Key & Chr(34) & ")"
                PrintJSONAccessors JSON(Key), Accessor
            ElseIf TypeName(JSON(Key)) = "Variant()" Then
                data = JSON(Key)
                For n = LBound(data) To UBound(data)
                    Accessor = Prefix & "(" & Chr(34) & Key & Chr(34) & ")"
                    ArrayAccessor = Prefix & "(" & Chr(34) & Key & Chr(34) & ")" & "(" & n & ")"
                    If TypeName(data(n)) = "Dictionary" Then
                        PrintJSONAccessors data(n), ArrayAccessor
                    Else
                        Debug.Print ArrayAccessor
                    End If
                Next
            Else
                Accessor = Prefix & "(" & Chr(34) & Key & Chr(34) & ")"
                Debug.Print Accessor
            End If
        Next
    End If
End Sub

用法:

 PrintJSONAccessors JSON, "?JSON"

MSScriptControl.ScriptControl 似乎只适用于 32 位系统。我想这就是 SIM 在他的 cmets 中所暗示的。虽然,我的回答是 IMO 正确的,但您应该忽略 cmets 的下一部分。

仅供参考: 我在Code Review 上发布了一个将JSON 解析为数组和字典Function to Return a JSON Like Objects Using VBA Collections and Arrays 的函数。它不能替代JsonConverter 或omegastripes 的JSON.Bas。它演示了您可以将JScript 代码添加到CreateObject("MSScriptControl.ScriptControl") 并使用它来处理JSON。

【讨论】:

  • 不错。 MSScriptControl.ScriptControl 是否适用于 64 位?我只考虑了 32 个。使用 ScriptControl 是否有任何风险?
  • @QHarrI 没有意识到这一点。无论出于何种原因,Office 365 拒绝安装我的系统的 64 位版本。谢谢你的信息,兄弟!
  • 我认为你在某个地方写了一个答案,你在 jsonconverter.bas 中给出了 json 对象的映射,即 [] = 集合等.....我想将一个 OP 推荐给你的答案,但不能找不到它。请问你能记得那个答案吗?
  • @QHarr 这是那个How to get, JSON values to Work in VBA-JSON?[How to get, JSON values to Work in VBA-JSON?](https://stackoverflow.com/a/53494208/9912714)
  • 没有但也有用
【解决方案2】:

试试代码:

    Set json = JsonConverter.ParseJson(s)
    For Each k In json(1)
        Debug.Print k & vbTab & json(1)(k)
    Next

更新

看看下面的例子。 JSON.bas模块导入VBA项目进行JSON处理。

Option Explicit

Sub Test()

    Dim sJSONString As String
    Dim vJSON
    Dim sState As String
    Dim aData()
    Dim aHeader()
    Dim vResult

    ' Read JSON sample from file C:\Test\sample.json
    sJSONString = ReadTextFile("C:\Test\sample.json", 0)
    ' Parse JSON sample
    JSON.Parse sJSONString, vJSON, sState
    If sState = "Error" Then
        MsgBox "Invalid JSON"
        End
    End If
    ' Get the 1st element from root [] array
    Set vJSON = vJSON(0)
    ' Convert raw JSON to 2d array and output to worksheet #1
    JSON.ToArray vJSON, aData, aHeader
    With Sheets(1)
        .Cells.Delete
        .Cells.WrapText = False
        OutputArray .Cells(1, 1), aHeader
        Output2DArray .Cells(2, 1), aData
        .Columns.AutoFit
    End With
    ' Flatten JSON
    JSON.Flatten vJSON, vResult
    ' Convert flattened JSON to 2d array and output to worksheet #2
    JSON.ToArray vResult, aData, aHeader
    With Sheets(2)
        .Cells.Delete
        .Cells.WrapText = False
        OutputArray .Cells(1, 1), aHeader
        Output2DArray .Cells(2, 1), aData
        .Columns.AutoFit
    End With
    MsgBox "Completed"

End Sub

Sub OutputArray(oDstRng As Range, aCells As Variant)

    With oDstRng
        .Parent.Select
        With .Resize(1, UBound(aCells) - LBound(aCells) + 1)
            .NumberFormat = "@"
            .Value = aCells
        End With
    End With

End Sub

Sub Output2DArray(oDstRng As Range, aCells As Variant)

    With oDstRng
        .Parent.Select
        With .Resize( _
                UBound(aCells, 1) - LBound(aCells, 1) + 1, _
                UBound(aCells, 2) - LBound(aCells, 2) + 1)
            .NumberFormat = "@"
            .Value = aCells
        End With
    End With

End Sub

Function ReadTextFile(sPath As String, lFormat As Long) As String

    ' lFormat -2 - System default, -1 - Unicode, 0 - ASCII
    With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, lFormat)
        ReadTextFile = ""
        If Not .AtEndOfStream Then ReadTextFile = .ReadAll
        .Close
    End With

End Function

顺便说一句,类似的方法适用于in other answers

【讨论】:

  • 我将其定义为变量,它适用于前三个项目,但在第 4 项它返回 450 错误“参数数量错误或属性分配无效。”
  • @Jerome 需要 JSON 样本以进一步了解和描述您需要访问的确切数据。
  • 刚刚发布的 JSON 示例
  • JSON.Bas 做得很好。我也喜欢你刮帖子的方式。 +1∞
  • @Jerome 代码的目的是直接从 SO 上的这篇文章中读取 JSON 示例。您的 PC 似乎出了点问题,所以我更改了代码,现在您应该将 JSON 示例保存到本地文件,请参阅代码中的 cmets。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-09
  • 2017-12-08
  • 1970-01-01
  • 2017-09-23
  • 1970-01-01
相关资源
最近更新 更多