Jump to content

A Non-Strict JSON UDF (JSMN)


Ward
 Share

Recommended Posts

#Include "Json.au3"

Local $Json = '{ "Headers":[ { "Name":"LOGICAL_NAME","Type":"VARCHAR2"}],"ColumnCount":1,"Rows":[ { "Cells":[ "ThisIsWhatIWant"],"CellCount":1}]}'
Local $Obj = Json_Decode($Json)

ConsoleWrite(Json_Get($Obj, '["Rows"][0]["Cells"][0]') & @LF)
ConsoleWrite(Json_Get($Obj, '.Rows[0].Cells[0]') & @LF)

ConsoleWrite(Json_Encode($Obj, $JSON_PRETTY_PRINT) & @LF)

新版 _ArrayAdd 的白痴作者,不管是誰,去死一死好了

 

Link to comment
Share on other sites

I'm learning JSON, so I wanted a ConsoleWriteJson( $StringJson ) , so I put it together, it may help the next guy ( you'd need to #include <JSON.au3> , but you know that. )

Edit1: I see I did not code anything in case of error.

Func ConsoleWriteJson($sJsonString, $sDesc = "", $iEcho = True)
    If $sDesc = "" Then $sDesc = 'ConsoleWriteJson'
    Local $obj = Json_Decode($sJsonString)
    Local $sOutGlobal = ""
    Return Json_Iterate($obj, '', $sOutGlobal, $sDesc, $iEcho)
EndFunc   ;==>ConsoleWriteJson

Func Json_Iterate($obj, $String, ByRef $sOutGlobal, $pre = "", $iEcho = True)
    Local $sOut = ""
    Local $temp, $i, $b
    If ($pre <> "") Then
        $sOutGlobal &= $pre & ": "
        If $iEcho Then ConsoleWrite($pre & ": ")
    EndIf
    $a = Json_Get_ShowResult($obj, $String, $sOutGlobal, $iEcho)
    If IsArray($a) Then
        For $i = 0 To UBound($a) - 1
            Json_Iterate($obj, $String & '[' & $i & ']', $sOutGlobal, $pre, $iEcho)
        Next
    ElseIf IsObj($a) Then
        $b = Json_ObjGetKeys($a)
        For $temp In $b
            Json_Iterate($obj, $String & '["' & $temp & '"]', $sOutGlobal, $pre, $iEcho)
        Next
    EndIf
    Return $sOutGlobal
EndFunc   ;==>Json_Iterate

Func Json_Get_ShowResult($Var, $Key, ByRef $sOutGlobal, $iEcho)
    Local $sOut = ""
    Local $Ret = Json_Getr($Var, $Key)
    If @error Then
        Switch @error
            Case 1
                $sOut &= "Error 1: key not exists" & @LF
                $sOutGlobal &= $sOut
                If $iEcho Then ConsoleWrite($sOut)
            Case 2
                $sOut &= "Error 2: syntax error" & @LF
                $sOutGlobal &= $sOut
                If $iEcho Then ConsoleWrite($sOut)
        EndSwitch
    Else
        $sOut &= $Key & " => " & VarGetType($Ret) & ": " & $Ret & @LF
        $sOutGlobal &= $sOut
        If $iEcho Then ConsoleWrite($sOut)
    EndIf
    Return $Ret
EndFunc   ;==>Json_Get_ShowResult

Func Json_Getr($Var, $Key)
    If Not $Key Then Return $Var
    Local $Match = StringRegExp($Key, "(^\[([^\]]+)\])", 3)
    If IsArray($Match) Then
        Local $Index = Json_Decode($Match[1])
        $Key = StringTrimLeft($Key, StringLen($Match[0]))
        If IsString($Index) And Json_IsObject($Var) And Json_ObjExists($Var, $Index) Then
            Local $Ret = Json_Getr(Json_ObjGet($Var, $Index), $Key)
            Return SetError(@error, 0, $Ret)
        ElseIf IsNumber($Index) And IsArray($Var) And $Index >= 0 And $Index < UBound($Var) Then
            Local $Ret = Json_Getr($Var[$Index], $Key)
            Return SetError(@error, 0, $Ret)
        Else
            Return SetError(1, 0, "")
        EndIf
    EndIf
    Return SetError(2, 0, "")
EndFunc   ;==>Json_Getr
Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

  • 3 months later...
  • 3 weeks later...

Thank you Ward for the time invested in this UDF, because I really do not understand the whole JSON thing yet :sweating:, so I am thankfull you do ;-)

I wanted to change some settings in the Chrome preferences-file, which turnout to be a JSON-file, so I am going to use your UDF! :) 

Link to comment
Share on other sites

  • 2 months later...

Hello,

It seems that having ExpandVarStrings or ExpandEnvStrings option set to 1 makes Json_encode() function crashes. I have to do this in order to make the function works :      

Opt("ExpandVarStrings",0)
Opt("ExpandEnvStrings",0)
Json_Encode($obj,$JSON_UNESCAPED_SLASHES))
Opt("ExpandVarStrings",1)
Opt("ExpandEnvStrings",1)

Otherwise the program simply crashes ( There is no AutoIT error, the program just crashes ). Are you aware of this possible bug ?

Regards. 

Edited by Xplode
Link to comment
Share on other sites

  • 2 months later...
  • 3 weeks later...

How do I list the last level of objects in the last level of keys?  As you can see I've commented out my last, of many, efforts.

 

#include "Json.au3"

$Json = '{"Air Elemental":{"layout":"normal","name":"Air Elemental","manaCost":"{3}{U}{U}","cmc":5,"colors":["Blue"],"type":"Creature — Elemental","types":["Creature"],"subtypes":["Elemental"],"text":"Flying","power":"4","toughness":"4","imageName":"air elemental","colorIdentity":["U"]},"Ancestral Recall":{"layout":"normal","name":"Ancestral Recall","manaCost":"{U}","cmc":1,"colors":["Blue"],"type":"Instant","types":["Instant"],"text":"Target player draws three cards.","imageName":"ancestral recall","colorIdentity":["U"]}'


$Obj = Json_Decode($Json)


If Json_IsObject($Obj) Then
    $Keys = Json_ObjGetKeys($Obj)
    For $i = 0 To UBound($Keys) - 1
        ConsoleWrite($Keys[$i] & @LF)

        $obects = Json_ObjGet($Obj, $Keys[$i])
        $KeysB = Json_ObjGetKeys($obects)

        For $h = 0 To UBound($KeysB) - 1
            ConsoleWrite('  ' & $KeysB[$h] & @LF)

;~          $obectsB = Json_ObjGet($obects, $KeysB[$h])
;~          $KeysC = Json_ObjGetKeys($obectsB)

;~          For $g = 0 To UBound($KeysC) - 1
;~              ConsoleWrite('      ' &  $KeysC[$g] & @LF)
;~          Next

        Next
    Next
EndIf

 

Edited by Jury
Link to comment
Share on other sites

How do I list the last level of objects in the last level of keys?

according to http://stackoverflow.com/questions/4317456/getting-the-last-item-in-a-javascript-object

No. Order is not guaranteed in JSON and most other key-value data structures, 
so therefore the last item could sometimes be carrot and
at other times be banana and so on. If you need to rely on ordering, 
your best bet is to go with arrays. 
The power of key-value data structures lies in accessing values by
their keys, not in being able to get the nth item of the object.

or play around with ConsoleWriteJson  https://www.autoitscript.com/forum/topic/148114-a-non-strict-json-udf-jsmn/?do=findComment&comment=1224189  above

Edit: I see that the string has game like values and if it's a bot, I just got me in trouble for helping, but you've been here since 2009 and would not even dream of that.

Edited by argumentum

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Hello.
I wonder if there is a quick way to get key knowing only the array element.
Here is JSON string:

{"result":{"tree":{"36":{"1958":[989,1649,2066,659,616]},"4":{"2317":[886,2350,2219,1520,795,1352,809,2521],"1960":[2197,1628,1538,2332,1416],"489":[1277],"1289":[2213,25,2105,2214]},"34":{"2354":[1271],"2362":[1265,1266,1267,1358,2363,1268,1673,1269,1270,1275,2364,1276,1274],"1264":[2358,2359,2360,2361],"2057":[2355,2474,2356,2357],"2413":[1501,1580,525]},"10":{"610":[628,1568,1542,2335,1544,1545,1546,1549,1597,1552,1550,1553,1554,617,1555,2017,1257,1258,2208,677,1255,1479,1261,614,1259,2065,1254,1260,2209,2210,1547,1548,2211,615],"1581":[1582,1583,1590,1587,1594,1591,1588,1596,1585,1586,2078,1929,1593,1592,1595],"1556":[1557,1560,1561,1653,1570,1654,1655,1656,1930,1931,1932,1562,1563,1626,1564,1565,1559,1566,1573,1567]},"33":{"395":[396,2322,2167,1993,2321],"2326":[1036,400,574],"2389":[2387,2388],"2327":[695,399,402,490,499],"2324":[2325,2342,530,2152],"2328":[403,1279,716,2165,401]}}}}

I know only array element, for example, 1583. I need to get key - 10

Here is how I do that for now:

#Include <Array.au3>
#Include <Json.au3> ; https://www.autoitscript.com/forum/topic/148114-a-non-strict-json-udf-jsmn/

$iForumID = 1583
$sJSON = '{"result":{"tree":{"36":{"1958":[989,1649,2066,659,616]},"4":{"2317":[886,2350,2219,1520,795,1352,809,2521],"1960":[2197,1628,1538,2332,1416],"489":[1277],"1289":[2213,25,2105,2214]},"34":{"2354":[1271],"2362":[1265,1266,1267,1358,2363,1268,1673,1269,1270,1275,2364,1276,1274],"1264":[2358,2359,2360,2361],"2057":[2355,2474,2356,2357],"2413":[1501,1580,525]},"10":{"610":[628,1568,1542,2335,1544,1545,1546,1549,1597,1552,1550,1553,1554,617,1555,2017,1257,1258,2208,677,1255,1479,1261,614,1259,2065,1254,1260,2209,2210,1547,1548,2211,615],"1581":[1582,1583,1590,1587,1594,1591,1588,1596,1585,1586,2078,1929,1593,1592,1595],"1556":[1557,1560,1561,1653,1570,1654,1655,1656,1930,1931,1932,1562,1563,1626,1564,1565,1559,1566,1573,1567]},"33":{"395":[396,2322,2167,1993,2321],"2326":[1036,400,574],"2389":[2387,2388],"2327":[695,399,402,490,499],"2324":[2325,2342,530,2152],"2328":[403,1279,716,2165,401]}}}}'

ConsoleWrite (_GetAPICategoryID ($iForumID) & @CRLF)


Func _GetAPICategoryID ($iffAPIForumID)
    $Obj = Json_ObjGet(Json_ObjGet(Json_Decode($sJSON), "result"), "tree")
    $aTMP15 = Json_ObjGetKeys ($Obj)
    $iFoundCategoryID = ""
    For $i = 0 To UBound ($aTMP15) -1
        $Obj1 = Json_ObjGet ($Obj, $aTMP15[$i])
        $aTMP16 = Json_ObjGetKeys ($Obj1)
            For $x = 0 To UBound ($aTMP16) -1
                $aTMP17 = Json_Get ($Obj1, '["' & $atmp16[$x] & '"]')
                If _ArraySearch ($aTMP17, $iffAPIForumID) <> -1 Then
                    $iFoundCategoryID = $aTMP15[$i]
                    ExitLoop
                EndIf
            Next
        If $iFoundCategoryID  <> "" Then ExitLoop
    Next
    Return $iFoundCategoryID
EndFunc ; _GetAPICategoryID ()

But this is too slow and not gracefully. Maybe there is better way? Thank you in advance

Link to comment
Share on other sites

  • 5 weeks later...

Hello. I want to say thank you for this UDF, I tried other, nothing worked. And this figured out thanks to the topic and running. But still faced with the problem and do not know how to do that. I hope will prompt

Have two json

json_1
{"response":{"status":1,"httpStatus":200,"data":[{"id":"10445","categories":{"6":{"id":"6","name":"D"},"27":{"id":"27","name":"I"}}},{"id":"10159","categories":{"20":{"id":"20","name":"W"}}}],"errors":[],"errorMessage":null}}

json_2
{"response":{"status":1,"httpStatus":200,"data":[{"id":"11930","countries":{"CH":{"id":"756","code":"CH","name":"Switzerland","regions":[]}}},{"id":"11928","countries":{"UK":{"id":"826","code":"UK","name":"United Kingdom","regions":[]},"HR":{"id":"191","code":"HR","name":"Croatia","regions":[]}}}],"errors":[],"errorMessage":null}}

from both must be received "name" in the first "categories", the second of the "countries"

my code

#include <Array.au3>
#include "JSON.au3"

$sJSON = FileRead(@ScriptDir & "\1.json")

$objJson = Json_Decode($sJSON)
$Response = Json_ObjGet($objJson, "response")
$Data = Json_ObjGet($Response, "data")
Local $N = UBound($Data)
Dim $List[$N]
    For $i = 0 To UBound($Data) - 1
        Local $objJson2 = $Data[$i]
        Local $Categories = Json_ObjGet($objJson2, "categories") ;or "countries" for json_2
        Local $nCat = Json_ObjGetKeys($Categories)
        Local $N = UBound($nCat)
        Dim $aCat[$N]
        For $j = 0 To UBound($nCat) - 1
            Local $objJson2 = Json_ObjGet($Categories, $nCat[$j])
            $aCat[$j] = Json_ObjGet($objJson2, "name")
        Next
        $strCat = ''
        For $j = 0 to UBound($aCat) - 1
            $strCat &= $aCat[$j] & ', ' 
        Next
        $strCat = StringTrimRight($strCat, 2)
        $List[$i] = $strCat 
     Next

_ArrayDisplay($List,"List")

For json_1 works fine. For json_2 function Json_ObjGetKeys an error

JSON.au3 "(342): ==> Variable must be of type" Object ".:
Return $ Object.Keys ()
Return $ Object ^ ERROR

I realized that the reason for this error is that the structure of "countries" objects letters "CH", "UK" and the like, and the structure of the "categories" - number
How do I get the data from "countries" in this situation?

Link to comment
Share on other sites

@addjon, many ask for an Arrayfied Json. Here is my take at it, that works with your string. ( may not work with others )

#include <JSon.au3>
#include <Array.au3>

Local $sJson_1 = '{"response":{"status":1,"httpStatus":200,"data":[{"id":"10445","categories":{"6":{"id":"6","name":"D"},"27":{"id":"27","name":"I"}}},{"id":"10159","categories":{"20":{"id":"20","name":"W"}}}],"errors":[],"errorMessage":null}}'
Local $sJson_2 = '{"response":{"status":1,"httpStatus":200,"data":[{"id":"11930","countries":{"CH":{"id":"756","code":"CH","name":"Switzerland","regions":[]}}},{"id":"11928","countries":{"UK":{"id":"826","code":"UK","name":"United Kingdom","regions":[]},"HR":{"id":"191","code":"HR","name":"Croatia","regions":[]}}}],"errors":[],"errorMessage":null}}'

Local $aReturned = JsonArrayfied($sJson_1)
_ArrayDisplay($aReturned, "$aReturned")

$aReturned = JsonArrayfied($sJson_2)
_ArrayDisplay($aReturned, "$aReturned")

Func JsonArrayfied($sJsonString, $iEcho = 0)
    Local $sConsoleWriteJson = ConsoleWriteJson($sJsonString, "", $iEcho)
    Local $n, $aLines = StringSplit($sConsoleWriteJson, @LF, 1)
    Local $aTemp, $iRow = 0, $iCol = 0, $m, $aJsonArrayfied[UBound($aLines) + 1][100] ; a lazy but efficient way to go about it
    For $n = 1 To $aLines[0]
        If StringInStr($aLines[$n], ":") + 2 > StringLen($aLines[$n]) Then ContinueLoop
        $aLines[$n] = StringReplace($aLines[$n], "][", "|")
        $aLines[$n] = StringReplace($aLines[$n], "]", "|")
        $aLines[$n] = StringReplace($aLines[$n], "[", "|")
        $aTemp = StringSplit($aLines[$n], "|")
        $iRow += 1
        For $m = 1 To $aTemp[0] - 1
            If $iCol < $m Then $iCol = $m
            $aJsonArrayfied[$iRow][$m - 1] = StringReplace($aTemp[$m], '"', '')
        Next
        $aJsonArrayfied[$iRow][$aTemp[0] - 1] = StringTrimLeft($aTemp[$aTemp[0]], StringInStr($aTemp[$aTemp[0]], ":") + 1)
        $aJsonArrayfied[$iRow][0] = StringMid($aTemp[$aTemp[0]], 5, StringInStr($aTemp[$aTemp[0]], ":") - 5)
    Next
    $aJsonArrayfied[0][0] = $iRow
    $aJsonArrayfied[0][1] = $iCol
    ReDim $aJsonArrayfied[$iRow + 1][$iCol + 1]
    Return $aJsonArrayfied
EndFunc   ;==>JsonArrayfied

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; added by me

Func ConsoleWriteJson($sJsonString, $sDesc = "", $iEcho = 1)
    Local $sOutGlobal
    If $sDesc = "" Then $sDesc = 'ConsoleWriteJson'
    Local $obj = Json_Decode($sJsonString)
    Json_Iterate($sOutGlobal, $obj, '', $sDesc, $iEcho)
    Return $sOutGlobal
EndFunc   ;==>ConsoleWriteJson

Func Json_Iterate(ByRef $sOutGlobal, $obj, $string, $pre = "", $iEcho = 1)
    Local $sOut = ""
    Local $temp, $i, $b
    If ($pre <> "") Then
        $sOut &= $pre & ": "
        If $iEcho Then ConsoleWrite($pre & ": ")
    EndIf
    $a = Json_Get_ShowResult($obj, $string, $sOutGlobal, $iEcho)
    If IsArray($a) Then
        For $i = 0 To UBound($a) - 1
            Json_Iterate($sOutGlobal, $obj, $string & '[' & $i & ']', $pre, $iEcho)
        Next
    ElseIf IsObj($a) Then
        $b = Json_ObjGetKeys($a)
        For $temp In $b
            Json_Iterate($sOutGlobal, $obj, $string & '["' & $temp & '"]', $pre, $iEcho)
        Next
    EndIf
    Return $sOutGlobal
EndFunc   ;==>Json_Iterate

Func Json_Get_ShowResult($Var, $Key, ByRef $sOutGlobal, $iEcho)
    Local $sOut = ""
    Local $Ret = Json_Getr($Var, $Key)
    If @error Then
        Switch @error
            Case 1
                $sOut &= "Error 1: key not exists" & @LF
                If $iEcho Then ConsoleWrite($sOut)
            Case 2
                $sOut &= "Error 2: syntax error" & @LF
                If $iEcho Then ConsoleWrite($sOut)
        EndSwitch
    Else
        $sOut &= $Key & " => " & VarGetType($Ret) & ": " & $Ret & @LF
        If $iEcho Then ConsoleWrite($sOut)
    EndIf
    $sOutGlobal &= $sOut ;& $Ret
    Return $Ret
EndFunc   ;==>Json_Get_ShowResult

Func Json_Getr($Var, $Key)
    If Not $Key Then Return $Var
    Local $Match = StringRegExp($Key, "(^\[([^\]]+)\])", 3)
    If IsArray($Match) Then
        Local $Index = Json_Decode($Match[1])
        $Key = StringTrimLeft($Key, StringLen($Match[0]))
        If IsString($Index) And Json_IsObject($Var) And Json_ObjExists($Var, $Index) Then
            Local $Ret = Json_Getr(Json_ObjGet($Var, $Index), $Key)
            Return SetError(@error, 0, $Ret)
        ElseIf IsNumber($Index) And IsArray($Var) And $Index >= 0 And $Index < UBound($Var) Then
            Local $Ret = Json_Getr($Var[$Index], $Key)
            Return SetError(@error, 0, $Ret)
        Else
            Return SetError(1, 0, "")
        EndIf
    EndIf
    Return SetError(2, 0, "")
EndFunc   ;==>Json_Getr

Hope it helps to get what you look for.

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...