В Excel VBA в Windows, как получить строковое представление JSON вместо «[объект объекта]» для проанализированных переменных JSON?

отвечая на мой собственный вопрос здесь.
Я проделал некоторую работу с JSON в Excel VBA и опубликовал множество выводов, которые я сделаю в формате вопросов и ответов.https://stackoverflow.com/help/self-answer http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/

Так что в другом месте на stackoverflow можно увидеть вопросы о синтаксическом анализе JSON в VBA, но они, кажется, пропускают хитрость или два.

Для начала я отказываюсь от использования пользовательских библиотек синтаксического анализа JSON и вместо этого использую метод Eval ScriptControl в качестве основы всего моего кода JSON. А также мы выражаем предпочтение родным решениям Microsoft.

Вот предыдущий вопросВ Excel VBA в Windows, как смягчить проблему точечного синтаксического анализа проанализированного JSON, нарушенного поведением заглавных букв в среде IDE? на котором строится этот вопрос. Он показывает, что использование VBA.CallByName более надежно, чем использование точечного синтаксиса для прохождения проанализированного объекта JSON. Также еще один предыдущий вопросВ Excel VBA на Windows, как пройти через анализируемый массив JSON? показывает, как его также можно использовать для доступа к элементам массива. Но CallByName возвращает любопытный тип переменной, который появляется в окне Watch как Object / JScriptTypeInfo, и если один тип Debug.Print в непосредственном окне (или наводит курсор на переменную), он получает неинформативный «[object Object]».

Как мы можем улучшить это и получить строковое представление JSON?

Вот скриншот того, что вы видите в окнах Immediate после Debug.Print (?) И если вы наводите курсор мыши на переменную.

Это Вопрос 3 из серии 5. Вот полная серия

Q1В Excel VBA в Windows, как смягчить проблему точечного синтаксического анализа проанализированного JSON, нарушенного поведением заглавных букв в среде IDE?

Q2В Excel VBA на Windows, как пройти через анализируемый массив JSON?

Q3В Excel VBA в Windows, как получить строковое представление JSON вместо «[объект объекта]» для проанализированных переменных JSON?

Q4В Windows Excel VBA как получить ключи JSON с предупреждением «Ошибка времени выполнения« 438 »: объект не поддерживает это свойство или метод»?

Q5В Excel VBA для Windows, для проанализированных переменных JSON, что это за JScriptTypeInfo?

 S Meaden08 июн. 2016 г., 21:31
Есть много вопросов, поэтому один большой вопрос будет неуместным. На самом деле есть пять вопросов и ответов

Ответы на вопрос(2)

Решение Вопроса

относящиеся к работе с проанализированными объектами JSON, используют подход мини-сценария, и мы можем использовать этот подход здесь.

Во-первых, мы признаем, что Дуглас Крокфорд является автором «Javascript: The Good Parts» (http://shop.oreilly.com/product/9780596517748.do) и является экспертом по JavaScript. Таким образом, мы рады принять его код в отношении строковой классификации. Мы можем получить его код с помощью простого HTTP-запроса Xml (обычно сокращенного до XHR) и передать возвращаемый результат в метод AddCode ScriptControl. Затем добавьте некоторый код, который позволяет нам переопределить представление по умолчанию «[object Object]», вызывая библиотеку Дугласа. И затем убедитесь, что мы динамически добавляем это переопределение ко всем нашим переменным JScriptTypeInfo, как к тому, что происходит из метода ScriptControl Eval, который мы переносим с помощью DecodeJsonString (), так и к тому, что происходит из VBA.CallByName, который мы переносим с помощью GetJSONObject ().

Таким образом,

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

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 GetJavaScriptLibrary("https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js")
        soScriptEngine.AddCode "function overrideToString(jsonObj) { jsonObj.toString = function() { return JSON.stringify(this); } }"
    End If
    Set GetScriptEngine = soScriptEngine
End Function

Private Function GetJavaScriptLibrary(ByVal sURL As String) As String

    Dim xHTTPRequest As MSXML2.XMLHTTP60
    Set xHTTPRequest = New MSXML2.XMLHTTP60
    xHTTPRequest.Open "GET", sURL, False
    xHTTPRequest.send
    GetJavaScriptLibrary = xHTTPRequest.responseText

End Function

Private Function DecodeJsonString(ByVal JsonString As String) As Object
    Dim oScriptEngine As ScriptControl
    Set oScriptEngine = GetScriptEngine

    Set DecodeJsonString = oScriptEngine.Eval("(" + JsonString + ")")

    Call oScriptEngine.Run("overrideToString", DecodeJsonString) '* this gives JSON rendering instead of "[object Object]"

End Function

Private Function GetJSONObject(ByVal obj As Object, ByVal sKey As String) As Object
    Dim objReturn As Object
    Set objReturn = VBA.CallByName(obj, sKey, VbGet)
    Call GetScriptEngine.Run("overrideToString", objReturn) '* this gives JSON rendering instead of "[object Object]"
    Set GetJSONObject = objReturn
End Function

Private Sub TestJSONParsingWithCallByName2()

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


    Dim objJSON As Object
    Set objJSON = DecodeJsonString(sJsonString)

    Stop


    Dim objKey2 As Object
    Set objKey2 = GetJSONObject(objJSON, "key2")
    Debug.Print objKey2
    Stop

End Sub

Вот снимок экрана с новым кодом, который показывает строковое преобразование переменных JScriptTypeInfo

 S Meaden20 авг. 2016 г., 11:44
Благодарю. Есть код для преобразования из JSON в Xml Domstackoverflow.com/questions/1773550/... но я не смог заставить его работать с Microsoft Script Control. Он будет работать с cscript.exe (что также облегчает отладку в Visual Studio), но это означает обстрел другого процесса.
 Logan Reed18 авг. 2016 г., 20:53
Спасибо за эту мини-серию Excel VBA / JSON - я нахожу это очень полезным, так как действительно трудно найти хорошую информацию по этой теме на SO. Теперь на SO есть раздел «Документация» - я предлагаю вам также опубликовать их там. Одна мысль, которая приходит мне в голову - есть ли способ взять JSON и преобразовать или проанализировать его в XML DOM, который изначально поддерживает VBA? Затем он позволяет использовать DOM для удобных манипуляций, а также XPath для запроса. Может ли это быть эффективно сделано с помощью этого подхода (т. Е. Может даже использовать JScript для преобразования JSON в XML)?

S Meaden, это то, что я искал, простой способ преобразования объекта JSON в строку. Я использовал ваши идеи и объединил их с моим кодом, но мне не понравилась идея создания соединения и загрузки скрипта каждый раз, когда мне нужно было создать объект JSON. поэтому я сжал код JSON2.js в функцию и использовал ее вместо того, что я вставляю в следующий, возможно, кому-то тоже понравится идея.

Private Function JSON2() As String
    'https://stackoverflow.com/questions/37711073/in-excel-vba-on-windows-how-to-get-stringified-json-respresentation-instead-of
    'https://raw.githubusercontent.com/douglascrockford/JSON-js/master/json2.js
    JSON2 = _
        "if(typeof JSON!==""object""){JSON={};}" _
        & "(function(){""use strict"";var rx_one=/^[\],:{}\s]*$/;var rx_two=/\\(?:[""\\\/bfnrt]|u[0-9a-fA-F]{4})/g;var rx_three=/""[^""\\\n\r]*""|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;var rx_four=/(?:^|:|,)(?:\s*\[)+/g;var rx_escapable=/[\\""\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;var rx_dangerous=/[\u0000\u00ad\u0600-\u0604\u070f\u17,b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;function f(n){return n<10?""0""+n:n;}" _
        & "function this_value(){return this.valueOf();}" _
        & "if(typeof Date.prototype.toJSON!==""function""){Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+""-""+" _
        & "f(this.getUTCMonth()+1)+""-""+" _
        & "f(this.getUTCDate())+""T""+" _
        & "f(this.getUTCHours())+"":""+" _
        & "f(this.getUTCMinutes())+"":""+" _
        & "f(this.getUTCSeconds())+""Z"":null;};Boolean.prototype.toJSON=this_value;Number.prototype.toJSON=this_value;String.prototype.toJSON=this_value;}" _
        & "var gap;var indent;var meta;var rep;function quote(string){rx_escapable.lastIndex=0;return rx_escapable.test(string)?""\""""+string.replace(rx_escapable,function(a){var c=meta[a];return typeof c===""string""?c:""\\u""+(""0000""+a.charCodeAt(0).toString(16)).slice(-4);})+""\"""":""\""""+string+""\"""";}" _
        & "function str(key,holder){var i;var k;var v;var length;var mind=gap;var partial;var value=holder[key];if(value&&typeof value===""object""&&typeof value.toJSON===""function""){value=value.toJSON(key);}" _
        & "if(typeof rep===""function""){value=rep.call(holder,key,value);}"
    JSON2 = JSON2 _
        & "switch(typeof value){case""string"":return quote(value);case""number"":return isFinite(value)?String(value):""null"";case""boolean"":case""null"":return String(value);case""object"":if(!value){return""null"";}" _
        & "gap+=indent;partial=[];if(Object.prototype.toString.apply(value)===""[object Array]""){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||""null"";}" _
        & "v=partial.length===0?""[]"":gap?""[\n""+gap+partial.join("",\n""+gap)+""\n""+mind+""]"":""[""+partial.join("","")+""]"";gap=mind;return v;}" _
        & "if(rep&&typeof rep===""object""){length=rep.length;for(i=0;i<length;i+=1){if(typeof rep[i]===""string""){k=rep[i];v=str(k,value);if(v){partial.push(quote(k)+(gap?"": "":"":"")+v);}}}}else{for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?"": "":"":"")+v);}}}}" _
        & "v=partial.length===0?""{}"":gap?""{\n""+gap+partial.join("",\n""+gap)+""\n""+mind+""}"":""{""+partial.join("","")+""}"";gap=mind;return v;}}" _
        & "if(typeof JSON.stringify!==""function""){meta={""\b"":""\\b"",""\t"":""\\t"",""\n"":""\\n"",""\f"":""\\f"",""\r"":""\\r"",""\"""":""\\\"""",""\\"":""\\\\""};JSON.stringify=function(value,replacer,space){var i;gap="""";indent="""";if(typeof space===""number""){for(i=0;i<space;i+=1){indent+="" "";}}else if(typeof space===""string""){indent=space;}" _
        & "rep=replacer;if(replacer&&typeof replacer!==""function""&&(typeof replacer!==""object""||typeof replacer.length!==""number"")){throw new Error(""JSON.stringify"");}" _
        & "return str("""",{"""":value});};}" _
        & "if(typeof JSON.parse!==""function""){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k;var v;var value=holder[key];if(value&&typeof value===""object""){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}" _
        & "return reviver.call(holder,key,value);}" _
        & "text=String(text);rx_dangerous.lastIndex=0;if(rx_dangerous.test(text)){text=text.replace(rx_dangerous,function(a){return""\\u""+" _
        & "(""0000""+a.charCodeAt(0).toString(16)).slice(-4);});}" _
        & "if(rx_one.test(text.replace(rx_two,""@"").replace(rx_three,""]"").replace(rx_four,""""))){j=eval(""(""+text+"")"");return(typeof reviver===""function"")?walk({"""":j},""""):j;}" _
        & "throw new SyntaxError(""JSON.parse"");};}}());"
End Function
 Chris Strickland16 июл. 2018 г., 21:26
Спасибо, таким образом, мне не нужно зависеть от этого кода, чтобы поддерживать его.

Ваш ответ на вопрос