1

Тема: AHK: Класс для работы с JSON

Создана аналогичная тема в Коллекции. Эта тема для возможной дискуссии, если в ней возникнет необходимость.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

2

Re: AHK: Класс для работы с JSON

Стоят упоминания моменты из другого обсуждения, начиная с этого, поднимающего эту тему?

3

Re: AHK: Класс для работы с JSON

В чем отличие от этого?

class JSON
{
	Decode(jsonStr)
	{
		SC := ComObjCreate("ScriptControl")
		SC.Language := "JScript"
		ComObjError(false)

		jsCode := "function arrangeForAhkTraversing(r){if(r instanceof Array){for(var a=0;a<r.length;++a)r[a]=arrangeForAhkTraversing(r[a]);return[" array ",r]}if(r instanceof Object){var n=[],e=[];for(var o in r)n.push(o),e.push(arrangeForAhkTraversing(r[o]));return[" object ",[n,e]]}return[typeof r,r]}"

		SC.ExecuteStatement(jsCode "; obj=" jsonStr)
		return this.convertJScriptObjToAhks( SC.Eval("arrangeForAhkTraversing(obj)") )
	}

	Encode(obj)
	{
		str := ""
		array := true
		for k in obj
		{
			if (k == A_Index)
				continue
			array := false
			break
		}
		for a, b in obj
			str .= (array ? "" : """" a """: ") . (IsObject(b) ? this.Encode(b) : """" b """") . ", "
		str := RTrim(str, " ,")
		return (array ? "[" str "]" : "{" str "}")
	}

	convertJScriptObjToAhks(jsObj)
	{
		if (jsObj[0] == "object")
		{
			obj := {}, keys := jsObj[1][0], values := jsObj[1][1]
			loop % keys.length
				obj[keys[A_INDEX-1]] := this.convertJScriptObjToAhks( values[A_INDEX-1] )
			return obj
		}
		else if (jsObj[0] == "array")
		{
			array := []
			loop % jsObj[1].length
				array.insert(this.convertJScriptObjToAhks( jsObj[1][A_INDEX-1] ))
			return array
		}
		else
			return jsObj[1]
	}
}
Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

4

Re: AHK: Класс для работы с JSON

KusochekDobra пишет:

Стоят упоминания моменты из другого обсуждения, начиная с этого, поднимающего эту тему?

Не знаю, вам как кажется? У меня обычно проблем не возникает, если учитывать, что JSON не поддерживает 16-ричные числа.

Phoenixxx_Czar пишет:

В чем отличие от этого?

Глвное отличие в том, что здесь используется объект ScriptControl, а он есть только для 32-битных приложений, соответственно, код не универсальный.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5

Re: AHK: Класс для работы с JSON

Ну а вообще по-простому для парсинга JSON можно такой вариант использовать:

json =
(
{
  "key": 6,
  "array": [1, 2, 3],
  "message": "Hello, World!"
}
)
oDoc := ComObjCreate("htmlfile")
oDoc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
try oDoc.parentWindow.eval("var message = JSON.parse('" . RegExReplace(json, "\R") . "').message;")
catch {
   MsgBox, Invalid json!
   Return
}
MsgBox, % oDoc.parentWindow.message
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Класс для работы с JSON

Можете скорректировать так, чтобы JSON.Stringify() возвращал строку преобразованную JSON.Parse() в исходное состояние, когда в ней встречаются "true", "false" или "null"?


str = {"read":true,"status":null}
oStr := JSON.Parse(str)
sStr := JSON.Stringify(oStr)
MsgBox % str "`n" sStr

7

Re: AHK: Класс для работы с JSON

Не думаю, что это возможно. Во-первых, -1 вместо true возвращает JS-объект по каким-то своим резонам после

oJSON := this.JS.("(" JsonString ")")

Во-вторых, даже если бы это было не так, в AHK true — эквивалент единицы:

MsgBox, % true

В-третьих, в AHK отсутствует аналог null, так что ему нечего показывать после

oStr := JSON.Parse(str)

Так что лучше просто не пользоваться подобными значениями.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: Класс для работы с JSON

Точно же, с "null" вообще двоякая ситуация получается в этой связи, если иметь ввиду именно пустое значение.
Благодарю за разъяснения.

9

Re: AHK: Класс для работы с JSON

А можно попросить Вас добавить "JSON.Stringify()", поддержку пользовательских символов для преодоления этого ограничения?
Например, объект формируется с заведомо некорректными строковыми значениями, в которых пользовательский символ предваряет начало сигнатуры строки, которую нужно изменить, если идущий за этим символом текст соответствует какому-то выбору. Что-то подобное этому:

str_origin = {"val_true":"_true","val_false":"_false","val_null":"_null","val_other":"_other"}
MsgBox % JSValues(str_origin, "_") "`n" JSValues(str_origin)

JSValues(str, sign := "") {
	if (sign) {
		reg_ex = "%sign%(true|false|null)"
		Return RegExReplace(str, reg_ex, "$1")
	} Return str
}

10 (изменено: teadrinker, 2019-02-18 17:07:49)

Re: AHK: Класс для работы с JSON

Не уверен, что это хороший вариант. А если в объекте попадётся реальная строка "_false"?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

11

Re: AHK: Класс для работы с JSON

Для того, эта возможность и будет опциональной. Применять изменение строки нужно только в случае, если это будет требовать пользователь сообщая признак, по которому значение будет идентифицироваться. К тому же, признак можно комбинировать несколькими символами, сводя к нулю такую вероятность.

Дело в том, что отправляя JSON на сервер, его API может истолковывать сериализованную таким образом строку, как некорректную, ожидая в местах значений "-1", "0" и "", соответствующие им true, false и null и его не попросить, чтобы он сделал поправку на синтаксис AHK, который для него чуть своеобразней, чем требуется, хотя семантически это очень близкие значения(?строгая типизация?). Приходится дополнительно обрабатывать для этого отправляемые данные таким нехитрым способом, чтобы избежать ненужной реакции. По крайней мере, я, как разработчик могу на своей стороне предпринять дополнительные мероприятия чтобы быть уверенным, что сформированная из объекта строка будет описана верно, ведь это я её формирую и если бы мой инструмент был чуть более гибок, предоставляя "из коробки" средства для доп обработки, то это было бы только в плюс!

Ко всему, превращение объекта в строку и нужно скорее только для обратной связи, а раз так, то это нисколько не повлияет на логику, которая строится на входящих данных.

12

Re: AHK: Класс для работы с JSON

Ну, если опционально, можно попробовать. Как будет время, добавлю.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

13

Re: AHK: Класс для работы с JSON

KusochekDobra пишет:

Можете скорректировать так, чтобы JSON.Stringify() возвращал строку преобразованную JSON.Parse() в исходное состояние, когда в ней встречаются "true", "false" или "null"?

teadrinker пишет:

Не думаю, что это возможно

Через ActiveScript можно:

js := new ActiveScript("JScript")
code =
(
var obj = JSON.parse('{"read":true,"status":null}'); 
var myJSON = JSON.stringify(obj);
)
js.Exec(code)
msgbox % js.myJSON



/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }
    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}

14

Re: AHK: Класс для работы с JSON

Здесь JSON.parse() возвращает не AHK-массив.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: Класс для работы с JSON

Ну и что из этого?

16 (изменено: teadrinker, 2019-02-18 21:07:45)

Re: AHK: Класс для работы с JSON

В смысле? Задача же была в AHK-массив перевести. Понятно, что JS умеет парсить JSON.

code =
(
var obj = JSON.parse('{"read":true,"status":null}'); 
var myJSON = JSON.stringify(obj);
)
oDoc := ComObjCreate("htmlfile")
oDoc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
try oDoc.parentWindow.eval(code)
catch {
   MsgBox, Error
   Return
}
MsgBox, % oDoc.parentWindow.myJSON
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

17

Re: AHK: Класс для работы с JSON

Так а почему мы не можем js объект перевести в ahk объект?

18

Re: AHK: Класс для работы с JSON

Ну попробуй этот объект перевести.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

19

Re: AHK: Класс для работы с JSON

js := new JsRT.IE
code =
(
var obj = JSON.parse('{"read":true,"status":null}');
var keys = Object.keys(obj);
)
js.Exec(code)
loop % js.keys.length
{
   key := (js.keys)[A_Index-1]
   msgbox % key ":" (js.obj)[key]
}




/*
 *  JsRT for AutoHotkey v1.1
 *
 *  Utilizes the JavaScript engine that comes with IE11.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class JsRT extends ActiveScript._base
{
    __New()
    {
        throw Exception("This class is abstract. Use JsRT.IE or JSRT.Edge instead.", -1)
    }
    
    class IE extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "jscript9", "ptr")
                throw Exception("Failed to load jscript9.dll", -1)
            if DllCall("jscript9\JsCreateRuntime", "int", 0, "int", -1
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("jscript9\JsCreateContext", "ptr", runtime, "ptr", 0, "ptr*", context)
            this._Initialize("jscript9", runtime, context)
        }
    }
    
    class Edge extends JsRT
    {
        __New()
        {
            if !this._hmod := DllCall("LoadLibrary", "str", "chakra", "ptr")
                throw Exception("Failed to load chakra.dll", -1)
            if DllCall("chakra\JsCreateRuntime", "int", 0
                , "ptr", 0, "ptr*", runtime) != 0
                throw Exception("Failed to initialize JsRT", -1)
            DllCall("chakra\JsCreateContext", "ptr", runtime, "ptr*", context)
            this._Initialize("chakra", runtime, context)
        }
        
        ProjectWinRTNamespace(namespace)
        {
            return DllCall("chakra\JsProjectWinRTNamespace", "wstr", namespace)
        }
    }
    
    _Initialize(dll, runtime, context)
    {
        this._dll := dll
        this._runtime := runtime
        this._context := context
        DllCall(dll "\JsSetCurrentContext", "ptr", context)
        DllCall(dll "\JsGetGlobalObject", "ptr*", globalObject)
        this._dsp := this._JsToVt(globalObject)
    }
    
    __Delete()
    {
        this._dsp := ""
        if dll := this._dll
        {
            DllCall(dll "\JsSetCurrentContext", "ptr", 0)
            DllCall(dll "\JsDisposeRuntime", "ptr", this._runtime)
        }
        DllCall("FreeLibrary", "ptr", this._hmod)
    }
    
    _JsToVt(valref)
    {
        VarSetCapacity(variant, 24, 0)
        DllCall(this._dll "\JsValueToVariant", "ptr", valref, "ptr", &variant)
        ref := ComObject(0x400C, &variant), val := ref[], ref[] := 0
        return val
    }
    
    _ToJs(val)
    {
        VarSetCapacity(variant, 24, 0)
        ref := ComObject(0x400C, &variant) ; VT_BYREF|VT_VARIANT
        ref[] := val
        DllCall(this._dll "\JsVariantToValue", "ptr", &variant, "ptr*", valref)
        ref[] := 0
        return valref
    }
    
    _JsEval(code)
    {
        e := DllCall(this._dll "\JsRunScript", "wstr", code, "uptr", 0, "wstr", "source.js"
            , "ptr*", result)
        if e
        {
            if DllCall(this._dll "\JsGetAndClearException", "ptr*", excp) = 0
                throw this._JsToVt(excp)
            throw Exception("JsRT error", -2, format("0x{:X}", e))
        }
        return result
    }
    
    Exec(code)
    {
        this._JsEval(code)
    }
    
    Eval(code)
    {
        return this._JsToVt(this._JsEval(code))
    }
    
    AddObject(name, obj, addMembers := false)
    {
        if addMembers
            throw Exception("AddMembers=true is not supported", -1)
        this._dsp[name] := obj
    }
}




/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }
    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}
class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}
_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}
_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}

20

Re: AHK: Класс для работы с JSON

Я вижу -1 и пустое значение.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: AHK: Класс для работы с JSON

Можно определять в js, что null = null, true = true и уже передавать в ahk с пометкой, что -1 - это не минус один, а true.
Кстати через htmlfile работает медленнее, чем через dllcall.

22

Re: AHK: Класс для работы с JSON

Malcev пишет:

Можно определять в js, что null = null, true = true и уже передавать в ahk с пометкой, что -1 - это не минус один, а true.

Так а в чём отличие от моего JSON?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

23

Re: AHK: Класс для работы с JSON

А у тебя разве определяется разница между true, единицей или минус единицей?

24

Re: AHK: Класс для работы с JSON

Если б определялось, тогда и говорить не о чем было. Я думал, ты привёл в пример ActiveScript, что через него это как-то удобнее делать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

25

Re: AHK: Класс для работы с JSON

Так я ж и пишу, что в ActiveScript мы можем проверять значения всех ключей и если значение true, то изменять его на znachenie_blah_blah_blah_true.
А в ahk уже проверять, если значение = znachenie_blah_blah_blah_true, то значит true.

26

Re: AHK: Класс для работы с JSON

А в чём отличие от моего JSON? Там тоже можно проверять.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

27

Re: AHK: Класс для работы с JSON

Не знаю.
Я сделал выводы из твоей фразы -

teadrinker пишет:

Не думаю, что это возможно.

28

Re: AHK: Класс для работы с JSON

При преобразовании строки в AHK-объект, нет никаких проблем. От JSON.Parse() не ожидается больше предоставленного. Преобразованные данные всё равно потом участвуют дальше на стороне AHK и мы вольны воспринимать их как нам заблагорассудится. Но если объект преобразуется в строку и отправляется на сервер, то там работают правила неподвластного нам контекста и ожидают именно null в качестве null, или true в роли true. Просто строка - "true" не корректна, так как это стока, а минус единица - значение типа int, в то время как ожидается bool. Проблема только в этом.

Нужно только добавить JSON.Stringify() второй параметр со значением по умолчанию равным любому эквиваленту не истинности и всегда возвращать результат "обычного" преобразования если оно не менялось, иначе, не оборачивать в двойные кавычки каждое переданное "true", "false" и "null" при формировании JSON-строки, либо зареплейсить регуляркой финишный вариант, принимая значение второго параметра в качестве признака, вместе с которым будет формироваться каждый такой "_true", "_false", или "_null", как в примере номер девять.

Что оптимальней, не возьмусь сказать. Может есть ещё какой-либо хороший подход. Но для начинки это была бы весьма годная приспособа.
Спасибо за ваше участие!

29

Re: AHK: Класс для работы с JSON

Вообще сейчас true и false из AHK-объекта преобразуются в 1 и 0.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30

Re: AHK: Класс для работы с JSON

Наверное, от разрядности зависит. Я по некоторым причинам, вынужден x64 AHK пользовать. Но в целом, на обтекаемость, это не влияет.

+ открыть спойлер

https://d.radikal.ru/d33/1902/ca/e4c25cc655a5.gif

31

Re: AHK: Класс для работы с JSON

На картинке обратная ситуация — перевод из JSON в AHK-объект.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

32

Re: AHK: Класс для работы с JSON

Верно.
Прошу прощения, невнимательно прочитал.

33

Re: AHK: Класс для работы с JSON

Почему данный класс заменяет русские буквы на \u****?
Из:
["test", 123, "расишь текст"]
Получается:
["test",123,"\u00F0\u00E0\u00F1\u00E8\u00F8\u00FC \u00F2\u00E5\u00EA\u00F1\u00F2"]

Это очень мешает в дальнейшем.
Класс который я предоставлял в посте #3 такой беды не делает, наверное именно по этому я его выбрал.. Но у одного человека он вызвал ошибку из-за "ScriptControl".

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

34

Re: AHK: Класс для работы с JSON

Вот специально для вас вариант через htmlfile, не конвертирует кириллицу:

obj := {ключ: [{ключ: "значение"}, {ключ: "значение"}] }
MsgBox, % sJSON := JSON.Stringify(obj)

obj := JSON.Parse(sJSON)
MsgBox, % obj["ключ", 1, "ключ"]

class JSON
{
   static JS := JSON._GetJScripObject()
   
   Parse(JsonString)  {
      try oJSON := this.JS.("(" JsonString ")")
      catch  {
         MsgBox, Wrong JsonString!
         Return
      }
      Return this._CreateObject(oJSON)
   }
   
   GetFromUrl(url, contentType := "", userAgent := "", body := "")  {
      ; в случае удачи будет возвращена JSON-строка, в случае ошибки — массив с одним элементом-строкой с описанием ошибки
      try  {
         XmlHttp := ComObjCreate("Microsoft.XmlHttp")
         XmlHttp.Open("GET", url, false)
         ( contentType && XmlHttp.SetRequestHeader("Content-Type", contentType) )
         ( userAgent && XmlHttp.SetRequestHeader("User-Agent", userAgent) )
         XmlHttp.Send(body)
      }
      catch e
         Return ["Error!`n" . e.Message]
      status := XmlHttp.Status
      Return status = 200 ? XmlHttp.ResponseText : ["Error! Status: " . status . ", ResponseText: " . XmlHttp.ResponseText]
   }
   
   Stringify(obj) {
      sObj := this._ObjToString(obj)
      Return this.JS.("JSON.stringify(" . sObj . ")")
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScripObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := ObjBindMethod(doc.parentWindow, "eval")
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.("delete ActiveXObject; delete GetObject;")
      JS.(JScript)
   }

   _CreateObject(ObjJS)  {
      res := ObjJS.IsArray()
      if (res = "")
         Return ObjJS
      
      else if (res = -1)  {
         obj := []
         Loop % ObjJS.length
            obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := ObjJS.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
      }
      Return obj
   }
}

Обнаружите баги — пишите.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

35

Re: AHK: Класс для работы с JSON

Новая версия, добавлена поддержка null, true, false, добавлены методы GetKey(), SetKey(), RemoveKey(). Если кто обнаружит баги — пишите.

sJson = { ключ: [{ключ: "значение"}, {ключ: true}] }

obj := JSON.Parse(sJSON)                           ; обычный способ, получаем ahk-объект
MsgBox, % obj["ключ", 2, "ключ"]                   ; -1 вместо true

jsObj := JSON.Parse(sJSON, true)                   ; второй параметр true — предполагается, что на выходе js-объект
jsObj["ключ"][0]["ключ"] := "value"                ; заменяем "значение" на "value"
MsgBox, % sJson := JSON.Stringify(jsObj, true)     ; второй параметр true — предполагается, что парсится js-объект, теперь "ключ: true", как положено

MsgBox, % sJson := JSON.RemoveKey(sJson, "ключ")   ; удаляем ключ "ключ", остаётся {}

MsgBox, % sJson := JSON.SetKey(sJSON, "key1", "'Привет'")
MsgBox, % sJson := JSON.SetKey(sJson, "key2", "true")

MsgBox, % JSON.GetKey(sJson, "key2")

MsgBox, % sJson := JSON.SetKey(sJson, "key3", "{'key1':'value', 'key2':['one', null]}")
MsgBox, % sJson := JSON.SetKey(sJson, "key3.key2[2]", "false")

MsgBox, % JSON.GetKey(sJson, "key3")
MsgBox, % JSON.GetKey(sJson, "key3.key2[2]")

MsgBox, % sJson := JSON.RemoveKey(sJson, "key3.key2[2]") ; удаляем третий (null-based) элемент массива в ключе key3.key2

class JSON
{
   static JS := JSON._GetJScripObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      try res := this.JS.eval("JSON.stringify((" . sJson . ")." . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      res := this.JS.eval("var obj = (" . sJson . "); obj." . key . "=" . value . "; JSON.stringify(obj)")
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      if RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); obj." . match1 . ".splice(" . match2 . ", 1); JSON.stringify(obj)")
      else
         res := this.JS.eval("var obj = (" . sJson . "); delete obj." . key . "; JSON.stringify(obj)")
      this.JS.eval("obj = ''")
      Return res
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScripObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(ObjJS)  {
      res := ObjJS.IsArray()
      if (res = "")
         Return ObjJS
      
      else if (res = -1)  {
         obj := []
         Loop % ObjJS.length
            obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := ObjJS.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

36 (изменено: teadrinker, 2019-06-25 05:43:36)

Re: AHK: Класс для работы с JSON

Некоторые исправления, добавлен метод Enum().

sJson = [null, false, {"key1": 5, "key2": true, "key3": "string"}, {"key1": 0, "key2": false, "key3": "AHK"}]

for k, v in JSON.Enum(sJson)
   MsgBox, % "key: " k "`nvalue: " v

for k, v in JSON.Enum(sJson)
   for k, v in JSON.Enum(v)
      MsgBox, % "key: " k "`nvalue: " v
   
for k, v in JSON.Enum(sJson, "[3]")
   MsgBox, % "key: " k "`nvalue: " v

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1)  {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj)  {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1)  {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

37

Re: AHK: Класс для работы с JSON

teadrinker пишет:

Вот специально для вас вариант через htmlfile, не конвертирует кириллицу:

obj := {ключ: [{ключ: "значение"}, {ключ: "значение"}] }
MsgBox, % sJSON := JSON.Stringify(obj)

obj := JSON.Parse(sJSON)
MsgBox, % obj["ключ", 1, "ключ"]

class JSON
{
   static JS := JSON._GetJScripObject()
   
   Parse(JsonString)  {
      try oJSON := this.JS.("(" JsonString ")")
      catch  {
         MsgBox, Wrong JsonString!
         Return
      }
      Return this._CreateObject(oJSON)
   }
   
   GetFromUrl(url, contentType := "", userAgent := "", body := "")  {
      ; в случае удачи будет возвращена JSON-строка, в случае ошибки — массив с одним элементом-строкой с описанием ошибки
      try  {
         XmlHttp := ComObjCreate("Microsoft.XmlHttp")
         XmlHttp.Open("GET", url, false)
         ( contentType && XmlHttp.SetRequestHeader("Content-Type", contentType) )
         ( userAgent && XmlHttp.SetRequestHeader("User-Agent", userAgent) )
         XmlHttp.Send(body)
      }
      catch e
         Return ["Error!`n" . e.Message]
      status := XmlHttp.Status
      Return status = 200 ? XmlHttp.ResponseText : ["Error! Status: " . status . ", ResponseText: " . XmlHttp.ResponseText]
   }
   
   Stringify(obj) {
      sObj := this._ObjToString(obj)
      Return this.JS.("JSON.stringify(" . sObj . ")")
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScripObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := ObjBindMethod(doc.parentWindow, "eval")
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.("delete ActiveXObject; delete GetObject;")
      JS.(JScript)
   }

   _CreateObject(ObjJS)  {
      res := ObjJS.IsArray()
      if (res = "")
         Return ObjJS
      
      else if (res = -1)  {
         obj := []
         Loop % ObjJS.length
            obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := ObjJS.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
      }
      Return obj
   }
}

Обнаружите баги — пишите.

Начались ошибки у людей:
-------------------------—
Script.exe
-------------------------—
Error: 0x80020006 - Неизвестное имя.

Specifically: eval

Line#
---> 6245: JS.("delete ActiveXObject; delete GetObject;")

Continue running the script?
-------------------------—
Да Нет
---------------------------

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

38 (изменено: teadrinker, 2019-08-06 15:20:12)

Re: AHK: Класс для работы с JSON

Здесь ошибки не должно быть, по идее, во всяком случае первый раз слышу о такой, выглядит, как какая-то системная проблема. Код для теста:

doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
JS := doc.parentWindow
MsgBox, % JS.eval("({""key"":""value""})").key ; MsgBox должно показать value

Если не сработает, значит что-то не так в системе.
Может, установлен IE ниже, чем 9?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

39

Re: AHK: Класс для работы с JSON

teadrinker, а почему не использовать ActiveScript?
Если делать для массового пользования, то полагаться на настройки и версиии IE не совсем надёжно.

40

Re: AHK: Класс для работы с JSON

Пока-то не было проблем. Попробую с ActiveScript.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

41

Re: AHK: Класс для работы с JSON

Phoenixxx_Czar, проверьте ещё:
IE —> Свойства браузера —> Безопасность:

https://i.imgur.com/GX1pCb6.png

Кнопка Другой...

https://i.imgur.com/0Zjulu7.png

Сценарии —> Активные сценарии —> Включить

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

42

Re: AHK: Класс для работы с JSON

Вспомнил, для IE ниже 9 версии надо инициализировать JavaScript интерпретатор:

doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=6"">")
msgbox % Doc.documentMode
JS := doc.parentWindow
JS.execScript("eval()")
MsgBox, % JS.eval("({""key"":""value""})").key ; MsgBox должно показать value

43 (изменено: Phoenixxx_Czar, 2019-08-06 18:32:37)

Re: AHK: Класс для работы с JSON

Первая версия ошибку выдала.

Вторая заработала.
1. 5.00...
2. value

И с классом который вы давали в первый раз тоже стало работать, без изменения кода (магия какая-то).


teadrinker, пока не пригодилось.

Win10: LTSC (21H2); AHK: ANSI (v1.1.36.02)

44

Re: AHK: Класс для работы с JSON

Malcev пишет:

а почему не использовать ActiveScript?

Поэтому:

ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)

/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }

    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}

class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}

_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}

_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1) {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj ) {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject() {
      JS := new ActiveScript("JScript")
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS) {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj) {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1) {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0) {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

45

Re: AHK: Класс для работы с JSON

Сравни:

ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1) {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj ) {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject() {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS) {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj) {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1) {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0) {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

46

Re: AHK: Класс для работы с JSON

Да, JScript конвертит.
Надо через JSRT.

new JsRT.IE

47

Re: AHK: Класс для работы с JSON

Так нормально, но

JsRT пишет:

Utilizes the JavaScript engine that comes with IE11.

Надо ещё проверить, независим ли он от настроек IE.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

48 (изменено: Malcev, 2019-08-08 13:27:41)

Re: AHK: Класс для работы с JSON

Нет поддержки int64:

val = {"a": 17849713558010739}
MsgBox, % JSON.Parse(val).a

49

Re: AHK: Класс для работы с JSON

У меня выдаёт

https://i.imgur.com/C3XFVAa.png

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

50

Re: AHK: Класс для работы с JSON

А должно 17849713558010739.

51

Re: AHK: Класс для работы с JSON

Так javascript выдаёт, и на JsRT так же.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

52

Re: AHK: Класс для работы с JSON

Ну да. Поэтому я и не написал, что баг, а то, что нет поддержки.
В библиотеке у Coco есть.

53

Re: AHK: Класс для работы с JSON

В консоли Хрома так же:

https://i.imgur.com/W2xSFv7.png

Ну вряд ли кому понадобится туда 64-битные числа пихать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

54

Re: AHK: Класс для работы с JSON

Апи инстаграма возвращает.
http://forum.script-coding.com/viewtopi … 48#p135348

55

Re: AHK: Класс для работы с JSON

teadrinker пишет:

У меня выдаёт

https://i.imgur.com/C3XFVAa.png

Я о том же вчера писал в соседней теме. Есть ли способ получать числа без изменений?

56

Re: AHK: Класс для работы с JSON

Malcev
Вот-вот)).

57

Re: AHK: Класс для работы с JSON

Да, использовать парсер от Coco:
https://github.com/cocobelgica/AutoHotkey-JSON

58

Re: AHK: Класс для работы с JSON

Оказалось, что если перед парсингом пройтись регулярным выражением и заключить нужные числа в кавычки

"pk": "\d+"

Тогда числа не меняются.

59

Re: AHK: Класс для работы с JSON

Malcev пишет:

использовать парсер от Coco

Так и не понял, куда какие вписывать объекты, ключи и т.д., для строки Инстаграма.

60 (изменено: Malcev, 2019-08-08 16:42:15)

Re: AHK: Класс для работы с JSON

Там свои тонкости и баги - если интересно, создавайте отдельную тему.
Оптимальный, наверное, вариант - взять оттуда алгоритм преобразования числовых значений в строчные и вставить в парсер от teadrinker.

61 (изменено: DD, 2019-08-08 20:30:10)

Re: AHK: Класс для работы с JSON

Я наверно числа буду закавычивать перед обработкой. Но вот на парсер от teadrinker хотелось бы перейти. Как в нём для строки Инстаграма выводить следующие данные из "caption" и "comments"?

"caption" [text, created_at, user_id, username]
"comments" [text, created_at, pk, user_id, username]

62

Re: AHK: Класс для работы с JSON

sJson = {"comment_likes_enabled": true, "comments": [{"pk": 17849713558010739, "user_id": 198212294, "text": "Тире ставится, когда есть пробел", "type": 0, "created_at": 1440283999, "created_at_utc": 1440283999, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": 198212294, "username": "artijado", "full_name": "Igor Malinin", "is_private": false, "profile_pic_url": "https://scontent-arn2-1.cdninstagram.com/vp/cc261c5894efaa68c8e5e7d40c02f103/5DE7C76F/t51.2885-19/s150x150/60960188_330080244351723_7306559494501171200_n.jpg?_nc_ht=scontent-arn2-1.cdninstagram.com", "profile_pic_id": "2053930082462014902_198212294", "is_verified": false, "latest_reel_media": 1565204355}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 0, "inline_composer_display_condition": "never"}], "comment_count": 1, "caption": {"pk": 17852726821010739, "user_id": 1545290738, "text": "Уточнила написание числовых интервалов в \"Справочнике издателя и автора\" Мильчина и Чельцовой: между цифрами ставится широченное тире (а я ставил минус из скромности, он всё же не так режет глаз своей шириной). Забавно, что при этом на обложке самого справочника красуется дефис.\n\n#книжныйдизайн #emdash #мильчин #издал #artlebedev", "type": 1, "created_at": 1447723802, "created_at_utc": 1447723802, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": 1545290738, "username": "addaald", "full_name": "Adda Ald", "is_private": false, "profile_pic_url": "https://scontent-arn2-1.cdninstagram.com/vp/5244ed9c2b65d6d8e016149616e58e77/5DC8DF29/t51.2885-19/s150x150/62204076_2392406174113939_623129493279604736_n.jpg?_nc_ht=scontent-arn2-1.cdninstagram.com", "profile_pic_id": "2064098053023530828_1545290738", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}


; одиночный ключ, сохраняет true/false/null
MsgBox, % JSON.GetKey(sJson, "comments[0].text")

; одиночный ключ из ahk-объекта, не сохраняет true/false/null
ahkObj := JSON.Parse(sJson)
MsgBox, % ahkObj.comments[1].text

; парсинг JSON, такой парсинг нужно использовать, чтобы сохранить true/false/null
for k, v in JSON.Enum(sJson, "comments[0]")
   MsgBox, % "key:" . A_Tab . k "`nvalue:" . A_Tab . v
MsgBox, 48,, Парсинг comments[0] завершён!

for k, v in JSON.Enum(sJson, "caption")
   MsgBox, % "key:" . A_Tab . k "`nvalue:" . A_Tab . v
MsgBox, 48,, Парсинг caption завершён!

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1)  {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj)  {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1)  {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder
+ DD

63

Re: AHK: Класс для работы с JSON

Парсинг ahk-объекта не приводил, надеюсь, с ним понятно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

64

Re: AHK: Класс для работы с JSON

Спасибо, мощно! Есть ли настройка, чтобы некоторые ключи брались без кавычек?


; одиночный ключ, сохраняет true/false/null
;MsgBox, % JSON.GetKey(sJson, "comments[0].text")

      text :=                    JSON.GetKey(sJson, "comments[0].text")
      created_at :=              JSON.GetKey(sJson, "comments[0].created_at")
      cmt_id :=                  JSON.GetKey(sJson, "comments[0].pk")
      comment_like_count :=      JSON.GetKey(sJson, "comments[0].comment_like_count")
      user_id :=                 JSON.GetKey(sJson, "comments[0].user_id")
      username :=                JSON.GetKey(sJson, "comments[0].user.username")
      full_name :=               JSON.GetKey(sJson, "comments[0].user.full_name")
      profile_pic_id :=          JSON.GetKey(sJson, "comments[0].user.profile_pic_id")
      profile_pic_url :=         JSON.GetKey(sJson, "comments[0].user.profile_pic_url")

      list .= created_at " | " text " | " cmt_id " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count "`n"
      msgbox % list

65 (изменено: DD, 2019-08-08 20:50:57)

Re: AHK: Класс для работы с JSON

Вроде так — но тогда числа начинают дробиться


ahkObj := JSON.Parse(sJson)
      text :=                    ahkObj.comments[1].text
      created_at :=              ahkObj.comments[1].created_at
      cmt_id :=                  ahkObj.comments[1].pk
      comment_like_count :=      ahkObj.comments[1].comment_like_count
      user_id :=                 ahkObj.comments[1].user_id
      username :=                ahkObj.comments[1].user.username
      full_name :=               ahkObj.comments[1].user.full_name
      profile_pic_id :=          ahkObj.comments[1].user.profile_pic_id
      profile_pic_url :=         ahkObj.comments[1].user.profile_pic_url

66

Re: AHK: Класс для работы с JSON

При использовании GetKey() и Enum() ключи выводятся как есть в тексте, со всеми кавычками. Без кавычек можно взять из ahk-объекта, но с потерей true/false/null.
Подобные задачи можно решать циклом:

for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
   %v% := JSON.GetKey(sJson, "comments[0]." . v)

for k, v in ["user_name", "full_name", "profile_pic_id", "profile_pic_url"]
   %v% := JSON.GetKey(sJson, "comments[0].user." . v)

list .= created_at " | " text " | " cmt_id " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count
msgbox % list
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

67

Re: AHK: Класс для работы с JSON

teadrinker, это выводилось из "comments" и "comments.user". А как из "caption" вывести?

68

Re: AHK: Класс для работы с JSON

Смотрите структуру JSON, определяйте путь к нужным ключам. Это удобно делать с помощью каких-либо плагинов к браузерам, например:

https://i.imgur.com/ymT6twi.png

Видим, что в comments все ключи находятся в списке (фигурные скобки), который, в свою очередь, находится в массиве (квадратные скобки), и является его первым ключом, поэтому путь через [0].
В caption другое устройство:

https://i.imgur.com/f6XuUqI.png

Предоставляю вам возможность самостоятельно догадаться, как правильно указать путь.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

69

Re: AHK: Класс для работы с JSON

Готово, спасибо за задачку) —

for k, v in ["text", "created_at", "user_id"]
   %v% := JSON.GetKey(sJson, "caption." . v)

for k, v in ["username", "full_name", "profile_pic_id"]
   %v% := JSON.GetKey(sJson, "caption.user." . v)

list .= created_at " | " text " | " full_name " | " username " | " user_id " | " profile_pic_id
msgbox % list

70 (изменено: DD, 2019-08-09 00:30:34)

Re: AHK: Класс для работы с JSON

teadrinker, а с чем связано что "username" повторяется, хотя они отличаются в JSON-строке? —
del

71

Re: AHK: Класс для работы с JSON

А там разве есть такой ключ — user_name, который у вас во втором цикле?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

72

Re: AHK: Класс для работы с JSON

Точно!)) Видимо, с 66-го комментария закралось)).

73 (изменено: DD, 2019-08-09 04:30:39)

Re: AHK: Класс для работы с JSON

Возник вопрос, как собирать все комменты в общую переменную? Вроде нужен цикл, но как его применить?

sJson = 
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
)


for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
   %v% := JSON.GetKey(sJson, "comments[0]." . v)

for k, v in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
   %v% := JSON.GetKey(sJson, "comments[0].user." . v)

comments .= created_at " | " text " | " pk " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count " | " profile_pic_url


all .= comments
msgbox % all




class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1)  {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj)  {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1)  {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}

74

Re: AHK: Класс для работы с JSON

Не понял, в чём вопрос. Как удобнее, так и собирайте.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

75 (изменено: DD, 2019-08-09 04:43:13)

Re: AHK: Класс для работы с JSON

В смысле, собрать по принципу, —

comments .= ...

когда в результате все они будут в виде списка. Почему-то после первого коммента, остальные пропускаются.

76 (изменено: teadrinker, 2019-08-09 13:26:41)

Re: AHK: Класс для работы с JSON

Я так и не понял, как должна выглядеть строка на выходе.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

77 (изменено: DD, 2019-08-09 14:33:19)

Re: AHK: Класс для работы с JSON

teadrinker
В предыдущем скрипте, которым я пользовался для JSON обработки, все комментарии выводились за один раз (см. код ниже), а в Вашем выводится только первый коммент (см. код в 73-м сообщении). Поэтому хотелось понять, как их выводить все разом.

ResponseText =
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}

)

edges := JSON.Parse(ResponseText).comments
MsgBox % ListValues(edges)
ListValues(edges) {
   list := ""
   For k, NDS1 in edges {
      text :=                    NDS1.text
      created_at :=              NDS1.created_at
      pk :=                      NDS1.pk
      comment_like_count :=      NDS1.comment_like_count
      user_id :=                 NDS1.user_id
      username :=                NDS1.user.username
      full_name :=               NDS1.user.full_name
      profile_pic_id :=          NDS1.user.profile_pic_id
     ;profile_pic_url :=         NDS1.user.profile_pic_url

      list .= created_at " `n" text " `n▌" pk " `n" full_name " `n" username " `n" user_id " `n" profile_pic_id " `n" comment_like_count "`n"

   } Return list
}

class JSON
{
   static JS := JSON._GetJScripObject()
   
   Parse(JsonString)  {
      try oJSON := this.JS.("(" JsonString ")")
      catch  {
         MsgBox, Wrong JsonString!
         Return
      }
      Return this._CreateObject(oJSON)
   }

   _GetJScripObject()  {
      VarSetCapacity(tmpFile, (MAX_PATH := 260) << !!A_IsUnicode, 0)
      DllCall("GetTempFileName", Str, A_Temp, Str, "AHK", UInt, 0, Str, tmpFile)
      
      FileAppend,
      (
      <component>
      <public><method name='eval'/></public>
      <script language='JScript'></script>
      </component>
      ), % tmpFile
      
      JS := ObjBindMethod( ComObjGet("script:" . tmpFile), "eval" )
      FileDelete, % tmpFile
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.("delete ActiveXObject; delete GetObject;")
      JS.(JScript)
   }

   _CreateObject(ObjJS)  {
      res := ObjJS.IsArray()
      if (res = "")
         Return ObjJS
      
      else if (res = -1)  {
         obj := []
         Loop % ObjJS.length
            obj[A_Index] := this._CreateObject(ObjJS[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := ObjJS.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(ObjJS[k])
      }
      Return obj
   }
}

78

Re: AHK: Класс для работы с JSON

Вот так тоже берется только первый комментарий, а не все поочередно —

loop, 5
{
for k, v in ["text", "created_at", "pk", "comment_like_count", "user_id"]
   %v% := JSON.GetKey(sJson, "comments[0]." . v)

for k, v in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
   %v% := JSON.GetKey(sJson, "comments[0].user." . v)

comments .= created_at " | " text " | " pk " | " full_name " | " username " | " user_id " | " profile_pic_id " | " comment_like_count " | " profile_pic_url "`n"
}

79

Re: AHK: Класс для работы с JSON

Как-то так:

sJson = 
(
{"comment_likes_enabled": true, "comments": [{"pk": "17940178414272410", "user_id": "10843926123", "text": "А я люблю многоточие. Помнишь, у Набокова:\"Следы на цыпочках ушедших слов\"?", "type": 0, "created_at": 1553931298, "created_at_utc": 1553931298, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "10843926123", "username": "kachalova_lubov", "full_name": "Любовь Качалова. Коуч", "is_private": false, "profile_pic_url": "https", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 3, "inline_composer_display_condition": "never"}, {"pk": "17988326917202163", "user_id": "1553194130", "text": "@kachalova_lubov именно «ушедших». В публицистике должно быть больше конкретики. Работая редактором, часто истребляла многоточия.", "type": 2, "created_at": 1553932059, "created_at_utc": 1553932059, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "inline_composer_display_condition": "never"}, {"pk": "18049568809024649", "user_id": "19127031", "text": "Многоточия да! Я в переписке всегда спрашиваю, что вы под этим .... имеете ввиду) больше мне люди так не пишут))", "type": 0, "created_at": 1553937465, "created_at_utc": 1553937465, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "19127031", "username": "tadddddyy", "full_name": "Tatiana Druzhnyaeva", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1879693248769800373_19127031", "is_verified": false, "latest_reel_media": 0}, "did_report_as_spam": false, "share_enabled": false, "has_liked_comment": false, "comment_like_count": 1, "inline_composer_display_condition": "never"}], "comment_count": 6, "caption": {"pk": "17884550440314611", "user_id": "1553194130", "text": "Нет ничего мерзее многоточий", "type": 1, "created_at": 1553930114, "created_at_utc": 1553930114, "content_type": "comment", "status": "Active", "bit_flags": 0, "user": {"pk": "1553194130", "username": "eafanasova", "full_name": "Евгения Афанасова", "is_private": false, "profile_pic_url": "https", "profile_pic_id": "1821320762041417783_1553194130", "is_verified": false}, "did_report_as_spam": false, "share_enabled": false, "has_translation": true}, "caption_is_edited": true, "has_more_comments": false, "has_more_headload_comments": false, "media_header_display": "none", "display_realtime_typing_indicator": true, "preview_comments": [], "can_view_more_preview_comments": false, "status": "ok"}
)

comments := ""
for each, comment in JSON.Enum(sJson, "comments") {
   for each, key in ["text", "created_at", "pk", "comment_like_count", "user_id"]
      %key% := JSON.GetKey(comment, key)

   user := JSON.GetKey(comment, "user")
   
   for each, key in ["username", "full_name", "profile_pic_id", "profile_pic_url"]
      %key% := JSON.GetKey(user, key)
   
   comments .= (comments ? "`n`n" : "") . "▌`n" . created_at         . "`n"
                                                . text               . "`n"
                                                . pk                 . "`n"
                                                . full_name          . "`n"
                                                . username           . "`n"
                                                . user_id            . "`n"
                                                . profile_pic_id     . "`n"
                                                . comment_like_count . "`n"
                                                . profile_pic_url
}

MsgBox, % comments

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1)  {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj )  {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject()  {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS)  {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj)  {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1)  {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0)  {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder
+ DD

80

Re: AHK: Класс для работы с JSON

Исправлен баг, возникающий в Stringify(), когда в ключах JSON-строки есть пробелы. Добавлен параметр indent в Stringify(), GetKey(), SetKey() и RemoveKey().

sjson = {"supportedDevices":{"Android":4,"iPhone OS":8,"Windows Phone":8}}

obj := JSON.Parse(sjson)
MsgBox, % sjson := JSON.Stringify(obj,, A_Tab)
MsgBox, % JSON.GetKey(sjson, "supportedDevices", "  ")

jsObj := JSON.Parse(sjson, true)
MsgBox, % sjson := JSON.Stringify(jsObj, true, "  ")

MsgBox, % sjson := JSON.SetKey(sjson, "accounts", "[{'email':'dfiveg@mail.ru','authorized':false,'lost':true}]", "  ")
MsgBox, % JSON.RemoveKey(sjson, "accounts[0].email", "   ")

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false, indent := "") {
      if js
         Return this.JS.JSON.stringify(obj, "", indent)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
      }
   }
   
   GetKey(sJson, key, indent := "") {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ",'','" . indent . "')")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value, indent := "") {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj,'','" . indent . "')" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key, indent := "") {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj,'','" . indent . "')")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj,'','" . indent . "')" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1) {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj ) {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject() {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS) {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj) {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1) {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0) {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

81 (изменено: teadrinker, 2019-09-16 03:40:32)

Re: AHK: Класс для работы с JSON

teadrinker пишет:

Исправлен баг, возникающий в Stringify(), когда в ключах JSON-строки есть пробелы.

Исправил эту же проблему в Enum(), добавил в Enum() параметр indent, добавил поддержку true, false и null (нужно использовать JSON.true, JSON.false, JSON.null).

obj := { supportedDevices: {Android: JSON.true, "iPhone OS": JSON.false, "Windows Phone": JSON.null} }

MsgBox, % sjson := JSON.Stringify(obj,, "   ")

sjson := JSON.SetKey(sjson, "accounts", "[{'email':'dfiveg@mail.ru','authorized':false,'lost':true}]")

for k, v in JSON.Enum(sjson,, "   ")
   MsgBox, % "key:`n" . k . "`n`nvalue:`n" . v

class JSON
{
   static JS := JSON._GetJScriptObject(), true := {}, false := {}, null := {}
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false, indent := "") {
      if (js && !RegExMatch(js, "\s"))
         Return this.JS.JSON.stringify(obj, "", indent)
      else {
         (RegExMatch(js, "\s") && indent := js)
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ",'','" . indent . "')")
      }
   }
   
   GetKey(sJson, key, indent := "") {
      if !this.VerifyJson(sJson)
         Return
      
      try Return this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ",'','" . indent . "')")
      catch
         MsgBox, Bad key:`n`n%key%
   }
   
   SetKey(sJson, key, value, indent := "") {
      if !this.VerifyJson(sJson)
         Return
      if !this.VerifyJson(value, true) {
         MsgBox, % "Bad value: " . value                      . "`n"
                 . "Must be a valid JSON string."             . "`n"
                 . "Enclose literal strings in quotes '' or """".`n"
                 . "As an empty string pass '' or """""
         Return
      }
      try {
         res := this.JS.eval( "var obj = (" . sJson . ");"
                            . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                            . "JSON.stringify(obj,'','" . indent . "')" )
         this.JS.eval("obj = ''")
         Return res
      }
      catch
         MsgBox, Bad key:`n`n%key%
   }
   
   RemoveKey(sJson, key, indent := "") {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      try {
         if !RegExMatch(key, "(.*)\[(\d+)]$", match)
            res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj,'','" . indent . "')")
         else
            res := this.JS.eval( "var obj = (" . sJson . ");" 
                               . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                               . "JSON.stringify(obj,'','" . indent . "')" )
         this.JS.eval("obj = ''")
         Return res
      }
      catch
         MsgBox, Bad key:`n`n%key%
   }
   
   Enum(sJson, key := "", indent := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try {
         jsObj := this.JS.eval("(" sJson ")" . conc)
         res := jsObj.IsArray()
         if (res = "")
            Return
         obj := {}
         if (res = -1) {
            Loop % jsObj.length
               obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "],'','" . indent . "')")
         }
         else if (res = 0) {
            keys := jsObj.GetKeys()
            Loop % keys.length
               k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "['" . k . "'],'','" . indent . "')")
         }
         Return obj
      }
      catch
         MsgBox, Bad key:`n`n%key%
   }
   
   VerifyJson(sJson, silent := false) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         if !silent
            MsgBox, Bad JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj ) {
         for k, v in ["true", "false", "null"]
            if (obj = this[v])
               Return v
            
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : """" . k . """:" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      
      for k, v in [["\", "\\"], [A_Tab, "\t"], ["""", "\"""], ["/", "\/"], ["`n", "\n"], ["`r", "\r"], [Chr(12), "\f"], [Chr(08), "\b"]]
         obj := StrReplace( obj, v[1], v[2] )

      Return """" obj """"
   }

   _GetJScriptObject() {
      static doc
      doc := ComObjCreate("htmlfile")
      doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
      JS := doc.parentWindow
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS) {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj) {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1) {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0) {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}

Сообщайте о замеченных багах.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

82

Re: AHK: Класс для работы с JSON

Исправлен баг с парсингом спецсимволов, добавлена обработка ошибок.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

83

Re: AHK: Класс для работы с JSON

teadrinker, если ты привяжешь свою библиотеку к chrome.ahk, то можно будет хорошо потестировать её, сравнивая результат получения страниц через селениум и через твою.

84

Re: AHK: Класс для работы с JSON

Что ты имеешь в виду под привязкой? Привязкой можно считать этот код.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

85

Re: AHK: Класс для работы с JSON

Под привязкой я имею в виду, чтобы при выполнении такого кода использовалась твоя библиотека:

ChromeInst := new Chrome()
PageInst := ChromeInst.GetPage()
PageInst.Call("Page.navigate", {"url": "http://delfi.lv"})
PageInst.WaitForLoad()
RootNode := PageInst.Call("DOM.getDocument").root
OuterHTML := PageInst.Call("DOM.getOuterHTML", {"nodeId": RootNode.nodeId}).OuterHTML

86

Re: AHK: Класс для работы с JSON

Так у меня работает. Я только путь к профилю ещё указываю.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

87 (изменено: Malcev, 2019-10-04 23:10:43)

Re: AHK: Класс для работы с JSON

В твоем коде если использовать JS версии IE > 9:

js := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")

выдает ошибку:

ahkObj := { ключ: [{ключ: "значение"}, {ключ: true}] }
MsgBox, % JSON.Stringify(ahkObj)

/*
 *  ActiveScript for AutoHotkey v1.1
 *
 *  Provides an interface to Active Scripting languages like VBScript and JScript,
 *  without relying on Microsoft's ScriptControl, which is not available to 64-bit
 *  programs.
 *
 *  License: Use, modify and redistribute without limitation, but at your own risk.
 */
class ActiveScript extends ActiveScript._base
{
    __New(Language)
    {
        if this._script := ComObjCreate(Language, ActiveScript.IID)
            this._scriptParse := ComObjQuery(this._script, ActiveScript.IID_Parse)
        if !this._scriptParse
            throw Exception("Invalid language", -1, Language)
        this._site := new ActiveScriptSite(this)
        this._SetScriptSite(this._site.ptr)
        this._InitNew()
        this._objects := {}
        this.Error := ""
        this._dsp := this._GetScriptDispatch()  ; Must be done last.
        try
            if this.ScriptEngine() = "JScript"
                this.SetJScript58()
    }

    SetJScript58()
    {
        static IID_IActiveScriptProperty := "{4954E0D0-FBC7-11D1-8410-006008C3FBFC}"
        if !prop := ComObjQuery(this._script, IID_IActiveScriptProperty)
            return false
        VarSetCapacity(var, 24, 0), NumPut(2, NumPut(3, var, "short") + 6)
        hr := DllCall(NumGet(NumGet(prop+0)+4*A_PtrSize), "ptr", prop, "uint", 0x4000
            , "ptr", 0, "ptr", &var), ObjRelease(prop)
        return hr >= 0
    }
    
    Eval(Code)
    {
        pvar := NumGet(ComObjValue(arr:=ComObjArray(0xC,1)) + 8+A_PtrSize)
        this._ParseScriptText(Code, 0x20, pvar)  ; SCRIPTTEXT_ISEXPRESSION := 0x20
        return arr[0]
    }
    
    Exec(Code)
    {
        this._ParseScriptText(Code, 0x42, 0)  ; SCRIPTTEXT_ISVISIBLE := 2, SCRIPTTEXT_ISPERSISTENT := 0x40
        this._SetScriptState(2)  ; SCRIPTSTATE_CONNECTED := 2
    }
    
    AddObject(Name, DispObj, AddMembers := false)
    {
        static a, supports_dispatch ; Test for built-in IDispatch support.
            := a := ((a:=ComObjArray(0xC,1))[0]:=[42]) && a[0][1]=42
        if IsObject(DispObj) && !(supports_dispatch || ComObjType(DispObj))
            throw Exception("Adding a non-COM object requires AutoHotkey v1.1.17+", -1)
        this._objects[Name] := DispObj
        this._AddNamedItem(Name, AddMembers ? 8 : 2)  ; SCRIPTITEM_ISVISIBLE := 2, SCRIPTITEM_GLOBALMEMBERS := 8
    }
    
    _GetObjectUnk(Name)
    {
        return !IsObject(dsp := this._objects[Name]) ? dsp  ; Pointer
            : ComObjValue(dsp) ? ComObjValue(dsp)  ; ComObject
            : &dsp  ; AutoHotkey object
    }
    
    class _base
    {
        __Call(Method, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Method](Params*)
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Get(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
                try
                    return (this._dsp)[Property, Params*]
                catch e
                    throw Exception(e.Message, -1, e.Extra)
        }
        
        __Set(Property, Params*)
        {
            if ObjHasKey(this, "_dsp")
            {
                Value := Params.Pop()
                try
                    return (this._dsp)[Property, Params*] := Value
                catch e
                    throw Exception(e.Message, -1, e.Extra)
            }
        }
    }
    
    _SetScriptSite(Site)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+3*A_PtrSize), "ptr", p, "ptr", Site)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptSite")
    }
    
    _SetScriptState(State)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+5*A_PtrSize), "ptr", p, "int", State)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::SetScriptState")
    }
    
    _AddNamedItem(Name, Flags)
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+8*A_PtrSize), "ptr", p, "wstr", Name, "uint", Flags)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::AddNamedItem")
    }
    
    _GetScriptDispatch()
    {
        hr := DllCall(NumGet(NumGet((p:=this._script)+0)+10*A_PtrSize), "ptr", p, "ptr", 0, "ptr*", pdsp)
        if (hr < 0)
            this._HRFail(hr, "IActiveScript::GetScriptDispatch")
        return ComObject(9, pdsp, 1)
    }
    
    _InitNew()
    {
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+3*A_PtrSize), "ptr", p)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::InitNew")
    }
    
    _ParseScriptText(Code, Flags, pvarResult)
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        hr := DllCall(NumGet(NumGet((p:=this._scriptParse)+0)+5*A_PtrSize), "ptr", p
            , "wstr", Code, "ptr", 0, "ptr", 0, "ptr", 0, "uptr", 0, "uint", 1
            , "uint", Flags, "ptr", pvarResult, "ptr", 0)
        if (hr < 0)
            this._HRFail(hr, "IActiveScriptParse::ParseScriptText")
    }
    
    _HRFail(hr, what)
    {
        if e := this.Error
        {
            this.Error := ""
            throw Exception("`nError code:`t" this._HRFormat(e.HRESULT)
                . "`nSource:`t`t" e.Source "`nDescription:`t" e.Description
                . "`nLine:`t`t" e.Line "`nColumn:`t`t" e.Column
                . "`nLine text:`t`t" e.LineText, -3)
        }
        throw Exception(what " failed with code " this._HRFormat(hr), -2)
    }
    
    _HRFormat(hr)
    {
        return Format("0x{1:X}", hr & 0xFFFFFFFF)
    }
    
    _OnScriptError(err) ; IActiveScriptError err
    {
        VarSetCapacity(excp, 8 * A_PtrSize, 0)
        DllCall(NumGet(NumGet(err+0)+3*A_PtrSize), "ptr", err, "ptr", &excp) ; GetExceptionInfo
        DllCall(NumGet(NumGet(err+0)+4*A_PtrSize), "ptr", err, "uint*", srcctx, "uint*", srcline, "int*", srccol) ; GetSourcePosition
        DllCall(NumGet(NumGet(err+0)+5*A_PtrSize), "ptr", err, "ptr*", pbstrcode) ; GetSourceLineText
        code := StrGet(pbstrcode, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstrcode)
        if fn := NumGet(excp, 6 * A_PtrSize) ; pfnDeferredFillIn
            DllCall(fn, "ptr", &excp)
        wcode := NumGet(excp, 0, "ushort")
        hr := wcode ? 0x80040200 + wcode : NumGet(excp, 7 * A_PtrSize, "uint")
        this.Error := {HRESULT: hr, Line: srcline, Column: srccol, LineText: code}
        static Infos := "Source,Description,HelpFile"
        Loop Parse, % Infos, `,
            if pbstr := NumGet(excp, A_Index * A_PtrSize)
                this.Error[A_LoopField] := StrGet(pbstr, "UTF-16"), DllCall("OleAut32\SysFreeString", "ptr", pbstr)
        return 0x80004001 ; E_NOTIMPL (let Exec/Eval get a fail result)
    }
    
    __Delete()
    {
        if this._script
        {
            DllCall(NumGet(NumGet((p:=this._script)+0)+7*A_PtrSize), "ptr", p)  ; Close
            ObjRelease(this._script)
        }
        if this._scriptParse
            ObjRelease(this._scriptParse)
    }
    
    static IID := "{BB1A2AE1-A4F9-11cf-8F20-00805F2CD064}"
    static IID_Parse := A_PtrSize=8 ? "{C7EF7658-E1EE-480E-97EA-D52CB4D76D17}" : "{BB1A2AE2-A4F9-11cf-8F20-00805F2CD064}"
}

class ActiveScriptSite
{
    __New(Script)
    {
        ObjSetCapacity(this, "_site", 3 * A_PtrSize)
        NumPut(&Script
        , NumPut(ActiveScriptSite._vftable("_vft_w", "31122", 0x100)
        , NumPut(ActiveScriptSite._vftable("_vft", "31125232211", 0)
            , this.ptr := ObjGetAddress(this, "_site"))))
    }
    
    _vftable(Name, PrmCounts, EIBase)
    {
        if p := ObjGetAddress(this, Name)
            return p
        ObjSetCapacity(this, Name, StrLen(PrmCounts) * A_PtrSize)
        p := ObjGetAddress(this, Name)
        Loop Parse, % PrmCounts
        {
            cb := RegisterCallback("_ActiveScriptSite", "F", A_LoopField, A_Index + EIBase)
            NumPut(cb, p + (A_Index-1) * A_PtrSize)
        }
        return p
    }
}

_ActiveScriptSite(this, a1:=0, a2:=0, a3:=0, a4:=0, a5:=0)
{
    Method := A_EventInfo & 0xFF
    if A_EventInfo >= 0x100  ; IActiveScriptSiteWindow
    {
        if Method = 4  ; GetWindow
        {
            NumPut(0, a1+0) ; *phwnd := 0
            return 0 ; S_OK
        }
        if Method = 5  ; EnableModeless
        {
            return 0 ; S_OK
        }
        this -= A_PtrSize     ; Cast to IActiveScriptSite
    }
    ;else: IActiveScriptSite
    if Method = 1  ; QueryInterface
    {
        iid := _AS_GUIDToString(a1)
        if (iid = "{00000000-0000-0000-C000-000000000046}"  ; IUnknown
         || iid = "{DB01A1E3-A42B-11cf-8F20-00805F2CD064}") ; IActiveScriptSite
        {
            NumPut(this, a2+0)
            return 0 ; S_OK
        }
        if (iid = "{D10F6761-83E9-11cf-8F20-00805F2CD064}") ; IActiveScriptSiteWindow
        {
            NumPut(this + A_PtrSize, a2+0)
            return 0 ; S_OK
        }
        NumPut(0, a2+0)
        return 0x80004002 ; E_NOINTERFACE
    }
    if Method = 5  ; GetItemInfo
    {
        a1 := StrGet(a1, "UTF-16")
        , (a3 && NumPut(0, a3+0))  ; *ppiunkItem := NULL
        , (a4 && NumPut(0, a4+0))  ; *ppti := NULL
        if (a2 & 1) ; SCRIPTINFO_IUNKNOWN
        {
            if !(unk := Object(NumGet(this + A_PtrSize*2))._GetObjectUnk(a1))
                return 0x8002802B ; TYPE_E_ELEMENTNOTFOUND
            ObjAddRef(unk), NumPut(unk, a3+0)
        }
        return 0 ; S_OK
    }
    if Method = 9  ; OnScriptError
        return Object(NumGet(this + A_PtrSize*2))._OnScriptError(a1)
    
    ; AddRef and Release don't do anything because we want to avoid circular references.
    ; The site and IActiveScript are both released when the AHK script releases its last
    ; reference to the ActiveScript object.
    
    ; All of the other methods don't require implementations.
    return 0x80004001 ; E_NOTIMPL
}

_AS_GUIDToString(pGUID)
{
    VarSetCapacity(String, 38*2)
    DllCall("ole32\StringFromGUID2", "ptr", pGUID, "str", String, "int", 39)
    return String
}

class JSON
{
   static JS := JSON._GetJScriptObject()
   
   Parse(sJson, js := false)  {
      if jsObj := this.VerifyJson(sJson)
         Return js ? jsObj : this._CreateObject(jsObj)
   }
   
   Stringify(obj, js := false) {
      if js
         Return this.JS.JSON.stringify(obj)
      else {
         sObj := this._ObjToString(obj)
         Return this.JS.eval("JSON.stringify(" . sObj . ")")
      }
   }
   
   GetKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      try res := this.JS.eval("JSON.stringify((" . sJson . ")" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . ")")
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      Return res
   }
   
   SetKey(sJson, key, value) {
      if !(this.VerifyJson(sJson) && this.VerifyJson(value))
         Return
      
      res := this.JS.eval( "var obj = (" . sJson . ");"
                         . "obj" . (SubStr(key, 1, 1) = "[" ? "" : ".") . key . "=" . value . ";"
                         . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   RemoveKey(sJson, key) {
      if !this.VerifyJson(sJson)
         Return
      
      sign := SubStr(key, 1, 1) = "[" ? "" : "."
      if !RegExMatch(key, "(.*)\[(\d+)]$", match)
         res := this.JS.eval("var obj = (" . sJson . "); delete obj" . sign . key . "; JSON.stringify(obj)")
      else
         res := this.JS.eval( "var obj = (" . sJson . ");" 
                            . "obj" . (match1 != "" ? sign . match1 : "") . ".splice(" . match2 . ", 1);"
                            . "JSON.stringify(obj)" )
      this.JS.eval("obj = ''")
      Return res
   }
   
   Enum(sJson, key := "") {
      if !this.VerifyJson(sJson)
         Return
      
      conc := key ? (SubStr(key, 1, 1) = "[" ? "" : ".") . key : ""
      try jsObj := this.JS.eval("(" sJson ")" . conc)
      catch {
         MsgBox, Wrong key:`n`n%key%
         Return
      }
      res := jsObj.IsArray()
      if (res = "")
         Return
      obj := {}
      if (res = -1) {
         Loop % jsObj.length
            obj[A_Index - 1] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "[" . (A_Index - 1) . "])")
      }
      else if (res = 0) {
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this.JS.eval("JSON.stringify((" sJson ")" . conc . "." . k . ")")
      }
      Return obj
   }
   
   VerifyJson(sJson) {
      try jsObj := this.JS.eval("(" sJson ")")
      catch {
         MsgBox, Wrong JSON string:`n`n%sJson%
         Return
      }
      Return IsObject(jsObj) ? jsObj : true
   }
   
   _ObjToString(obj) {
      if IsObject( obj ) {
         isArray := true
         for key in obj {
            if IsObject(key)
               throw Exception("Invalid key")
            if !( key = A_Index || isArray := false )
               break
         }
         for k, v in obj
            str .= ( A_Index = 1 ? "" : "," ) . ( isArray ? "" : k . ":" ) . this._ObjToString(v)

         Return isArray ? "[" str "]" : "{" str "}"
      }
      else if !(obj*1 = "" || RegExMatch(obj, "\s"))
         Return obj
      else
         Return """" obj """"
   }

   _GetJScriptObject() {
      JS := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")
      JSON._AddMethods(JS)
      Return JS
   }

   _AddMethods(ByRef JS) {
      JScript =
      (
         Object.prototype.GetKeys = function () {
            var keys = []
            for (var k in this)
               if (this.hasOwnProperty(k))
                  keys.push(k)
            return keys
         }
         Object.prototype.IsArray = function () {
            var toStandardString = {}.toString
            return toStandardString.call(this) == '[object Array]'
         }
      )
      JS.eval(JScript)
   }

   _CreateObject(jsObj) {
      res := jsObj.IsArray()
      if (res = "")
         Return jsObj
      
      else if (res = -1) {
         obj := []
         Loop % jsObj.length
            obj[A_Index] := this._CreateObject(jsObj[A_Index - 1])
      }
      else if (res = 0) {
         obj := {}
         keys := jsObj.GetKeys()
         Loop % keys.length
            k := keys[A_Index - 1], obj[k] := this._CreateObject(jsObj[k])
      }
      Return obj
   }
}

88

Re: AHK: Класс для работы с JSON

Ничего не понял. А зачем использовать IE > 9 ?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

89

Re: AHK: Класс для работы с JSON

Решил проверить может уберется конвертация символов.
Сейчас у тебя работает на движке 5.8.

JS := new ActiveScript("JScript")
MsgBox % js.ScriptEngineMajorVersion() "." js.ScriptEngineMinorVersion()

90

Re: AHK: Класс для работы с JSON

Почему?

doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
MsgBox, % doc.documentMode 
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

91

Re: AHK: Класс для работы с JSON

Я имею в виду в коде 44.

92 (изменено: teadrinker, 2019-10-05 00:15:36)

Re: AHK: Класс для работы с JSON

А что это за CLSID? Почему он будет запускать версию > 9? Я у себя в реестре вижу соответствие ему
ProgID: JScript
InprocServer32: C:\Windows\System32\jscript9.dll
В любом случае так мы используем JScript, а в моём коде чистый Javascript.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

93

Re: AHK: Класс для работы с JSON

; Show which scripting engine is in use; probably "5.8".
ShowVersion(js)
; To use the newer IE9+ version of JScript, use the following.
js := new ActiveScript("{16d51579-a30b-4c8b-a276-0ff4dc41e755}")
ShowVersion(js)
ShowVersion(as) {
    MsgBox % as.ScriptEngineMajorVersion() "." as.ScriptEngineMinorVersion()
}

https://github.com/Lexikos/ActiveScript … xample.ahk

94

Re: AHK: Класс для работы с JSON

А, ну значит в этой версии JScript объект JSON не определён по какой-то причине.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

95

Re: AHK: Класс для работы с JSON

Через htmlfile работает:

doc := ComObjCreate("htmlfile")
doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=11"">")
JS := doc.parentWindow

MsgBox, % JS.eval("JSON.stringify({key:'value'})")
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

96

Re: AHK: Класс для работы с JSON

Наверное, действительно, это версия урезанная.
Если через ActiveScript получать, то надо использовать JsRT.IE или JsRT.Edge.

97 (изменено: Malcev, 2021-02-09 09:58:03)

Re: AHK: Класс для работы с JSON

teadrinker, так-как json может быть опасным, предлагаю проверять его на наличие опасного кода по алгоритму json2.js.
https://github.com/douglascrockford/JSO … n2.js#L473

98

Re: AHK: Класс для работы с JSON

Думаешь, в контексте AHK в этом есть смысл? Не логичнее просто не принимать json из непроверенных источников?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

99

Re: AHK: Класс для работы с JSON

teadrinker пишет:

Думаешь, в контексте AHK в этом есть смысл?

Не совсем понял, что ты имеешь в виду под контекстом AHK.
Я, конечно, не вирусописатель, но при "правильных" настройках IE, можно неприятных вещей натворить.

teadrinker пишет:

Не логичнее просто не принимать json из непроверенных источников?

А разве сейчас можно кому-либо на 100% доверять?

100

Re: AHK: Класс для работы с JSON

Имею в виду, что для AHK использование JSON, полученного из интернета, не такая частая ситуация, это же не web-разработка.

Malcev пишет:

А разве сейчас можно кому-либо на 100% доверять?

Не доверяешь — не запускай. Я постоянно пользуюсь JSON только из Гугл-транслейта, и то его не запускаю, а разбираю парсингом. За весь свой опыт не помню ситуации, чтобы был риск запустить из JSON вредоносный код. Можешь привести пример таковой?
Кстати, у нас же было изначально

JS.("delete ActiveXObject; delete GetObject;")

Это не решает проблему?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder