Jump to content

This site uses cookies. By continuing to browse the site you are agreeing to our use of cookies. Find out more here. X
X


Photo

JSMN - A Non-Strict JSON UDF

JSON JSMN

  • Please log in to reply
35 replies to this topic

#1 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 07 February 2013 - 11:11 AM

Introduction
JSON (Javascript Object Notation) is a popular data-interchange format and supported by a lot of script languages. On AutoIt, there is already a JSON UDF written by Gabriel Boehme. It is good but too slow, and not supports unicode and control characters very well. So I write a new one (and of course, fast one as usual).

I use a machine version of JSON parser called "jsmn", so this UDF also called JSMN. jsmn not only supports standard JSON, but also accepts some non-strict JSON string. See below for example.

Decoding Function


Jsmn_Decode($Json, $InitTokenCount = 1000)

$Json can be a standard or non-standard JSON string. For example, it accepts:

{     server: example.com     port: 80     message: "this looks like a config file" }

$InitTokenCount is only a suggestion of initial token counts. The decoder will allocate more memory automatically if it encounters $JSMN_ERROR_NOMEM error message.

Except object and null, other JSON data type will be decoded into corresponding AutoIt variable, including array, string, number, true, and false. JSON object will be decoded into "Windows Scripting Dictionary Object" retuned from ObjCreate("Scripting.Dictionary"), and null will be decoded into "Default" keyword. AutoIt build-in functions like IsArray, IsBool, etc. can be used to check the returned data type. But for Object and Null, Jsmn_IsObject() and Jsmn_IsNull() should be used. (COM object can't store "Default" keyword, but it will convert to a null object and that can be detected by Jsmn_IsNull() function).

If the input JSON string is invalid, @Error will be set to $JSMN_ERROR_INVAL. And if the input JSON string is not finish (maybe read from stream?), @Error will be set to $JSMN_ERROR_PART.

Encoding Function

Jsmn_Encode($Data, $Option = 0, $Indent = "\t", $ArraySep = ",\r\n", $ObjectSep = ",\r\n", $ColonSep = ": ")

$Data can be a string, number, bool, defualt(null), 1D arrry, or "Scripting.Dictionary" COM object. Ptr will be converted to number, Binary will be converted to string in UTF8 encoding. Other unsupported types like 2D array, dllstruct or object will be encoded into null.

$Option is bitmask consisting following constant:

$JSMN_UNESCAPED_UNICODE ; Encode multibyte Unicode characters literally $JSMN_UNESCAPED_SLASHES ; Don't escape / $JSMN_HEX_TAG ; All < and > are converted to \u003C and \u003E $JSMN_HEX_AMP ; All &amp;amp;amp;amp;s are converted to \u0026 $JSMN_HEX_APOS ; All ' are converted to \u0027 $JSMN_HEX_QUOT ; All " are converted to \u0022 $JSMN_PRETTY_PRINT ; Use whitespace in returned data to format it $JSMN_STRICT_PRINT ; Make sure returned JSON string is RFC4627 compliant $JSMN_UNQUOTED_STRING ; Output unquoted string if possible (conflicting with $JSMN_STRICT_PRINT)

Most encoding option have the same means like PHP's json_enocde() function. When $JSMN_PRETTY_PRINT is set, output format can be change by other 4 parameters ($Indent, $ArraySep, $ObjectSep, and $ColonSep). Because these 4 output format parameters will be checked inside Jsmn_Encode() function, returned string will be always accepted by Jsmn_Decode(). $JSMN_UNQUOTED_STRING can be used to output unquoted string that also accetped by Jsmn_Decode().

$JSMN_STRICT_PRINT is used to check output format setting and avoid non-standard JSON output. So this option is conflicting with $JSMN_UNQUOTED_STRING.

Other Help Functions

Jsmn_ObjTo2DArray(ByRef $Object) Jsmn_ObjFrom2DArray(ByRef $Array)

This UDF use "Scripting.Dictionary" COM object for represent JSON object, but Gabriel Boehme's UDF use 2D Array. So I add these two function to convert between them. Old scripts can use this new UDF without painful rewriting.
 

Jsmn_ObjCreate() Jsmn_ObjPut(ByRef $Object, $Key, $Value) Jsmn_ObjGet(ByRef $Object, $Key) Jsmn_ObjDelete(ByRef $Object, $Key) Jsmn_ObjExists(ByRef $Object, $Key) Jsmn_ObjGetCount(ByRef $Object) Jsmn_ObjGetKeys(ByRef $Object) Jsmn_ObjClear(ByRef $Object)

These functions are just shells of "Scripting.Dictionary" COM object. You can use these functions if you are not already familiar with it.
 

== Update 2013/05/19 ==

 

* Add Jsmn_Encode() option "$JSMN_UNESCAPED_ASCII". Now the default output of Jsmn_Encode() is exactly the same as PHP's json_encode() function (for example, chr(1) will be encoded into \u0001).

$JSMN_UNESCAPED_ASCII ; Don't escape ascii charcters between chr(1) ~ chr(0x1f)

Attached File  JSMN.zip   7.88KB   223 downloads

Attached File  JSMN.zip   8.03KB   836 downloads


Edited by Ward, 19 May 2013 - 01:14 PM.

  • James, matwachich, Danyfirex and 1 other like this







#2 Mat

Mat

    43 38 48 31 30 4E 34 4F 32

  • MVPs
  • 5,043 posts

Posted 07 February 2013 - 04:27 PM

:thumbsup:

#3 whizter

whizter

    Seeker

  • Normal Members
  • 5 posts

Posted 24 March 2013 - 11:23 AM

Hey,

thanks for the UDF, good work. But I have problems with a specific json source -> http://data.mtgox.com/api/1/BTCUSD/depth
I can't extract the values. First I used the other JSON UDF from Gabriel Boehme and I have exactly the same problems.
I get an empty array with ~500 elements when I get into the asks or bids arrays.

This is what I'm doing to get the data:
Func getArrayFromJson($URI,$reqAuth,$post="") $result = Jsmn_Decode(goxRequest($URI,_Iif($reqAuth>0,getNonce()&$post,""))) $result = Jsmn_ObjTo2DArray($result) If (Not IsArray($result) Or UBound($result) = 0) Then MsgBox(0,"Error: "&$URI,$result) Return 0 Else If(NOT IsArray($result[2][1])) Then MsgBox(0,$URI,$result[2][1]) Return 0 EndIf Return $result[2][1] EndIf EndFunc


It works on every other API without problems, just not with this one.

#4 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 28 March 2013 - 12:43 PM

Try this code, then your will see every element is decoded succeed.
#Include "JSMN.au3" Local $Json = BinaryToString(InetRead("<a href='http://data.mtgox.com/api/1/BTCUSD/depth' class='bbc_url' title='External link' rel='nofollow external'>http://data.mtgox.com/api/1/BTCUSD/depth"</a>), 4) Local $Obj = Jsmn_Decode($Json) ConsoleWrite(Jsmn_Encode($Obj, $JSMN_PRETTY_PRINT))


Then why you got empty output?
Because in your code, "$result[2][1]" is an array, so you can't use MsgBox to output it.

#5 savain

savain

    Seeker

  • Normal Members
  • 3 posts

Posted 11 April 2013 - 06:01 AM

What for an impolite user!

Thanks it works very fine! Randomly, I need it for Mt. Gox Too ;)

#6 mmscripting

mmscripting

    Seeker

  • Normal Members
  • 2 posts

Posted 18 May 2013 - 05:54 PM

Hey Ward,

 

fist of all thanks for JSON extension! I appreciate the work you spent to help this community with your programming skills!

 

I am new to this forum and autoit is one of my personal interests so I would like to say "Hello everyone" :bye:.

 

Maybe someone can help me with my question?:

I want to query a JSON wheather service to get the actual wheater and for future use also the forecast.

For this I use OperWheatherMap API and build my JSON, e.g. for today in Berlin:

{"cod":"200","message":0.0268,"city":{"id":2950159,"name":"Berlin","coord":{"lon":13.41053,"lat":52.524368},"country":"DE","population":1000000},"cnt":1,"list":[{"dt":1368874800,"temp":{"day":13.22,"min":10.57,"max":13.22,"night":10.57,"eve":12.49,"morn":13.22},"pressure":1015.76,"humidity":100,"weather":[{"id":501,"main":"Rain","description":"mäßiger Regen","icon":"10"}],"speed":7.08,"deg":269,"clouds":92,"rain":7.75}]}

My special interest lays in the params "temp":{"day":13.22,"min":10.57,"max":13.22 ... and  "description":"mäßiger Regen" but I am not able to extract this information using JSMN.

 

My code:

#include <JSMN.au3> ; Anfrage von Wetter mit JSON Antwort: http://api.openweathermap.org/data/2.5/forecast/daily?q=Berlin&mode=json&units=metric&cnt=1&lang=de local $Json1 = '{"cod":"200","message":0.0268,"city":{"id":2950159,"name":"Berlin","coord":{"lon":13.41053,"lat":52.524368},"country":"DE","population":1000000},"cnt":1,"list":[{"dt":1368874800,"temp":{"day":13.22,"min":10.57,"max":13.22,"night":10.57,"eve":12.49,"morn":13.22},"pressure":1015.76,"humidity":100,"weather":[{"id":501,"main":"Rain","description":"mäßiger Regen","icon":"10"}],"speed":7.08,"deg":269,"clouds":92,"rain":7.75}]}' Local $objJson = Jsmn_Decode($Json1) If (Jsmn_IsObject($objJson)) Then     ConsoleWrite('Data1:' & Jsmn_ObjGetKeys($objJson) & @CRLF)     ConsoleWrite('Data2:' & Jsmn_ObjGet($objJson, "message") & @CRLF)     ConsoleWrite('Data3:' & Jsmn_ObjGetCount($objJson) & @CRLF)     ConsoleWrite('Data4:' & Jsmn_ObjExists($objJson, "message") & @CRLF) Else     ConsoleWrite("Kein Objekt") EndIf

The according console output:

Data1: Data2:0.0268 Data3:5 Data4:True

So the first level was queried successfully but if I want to query the params mentioned above I get an empty result:

 

AutoIt:

#include <JSMN.au3> ; Anfrage von Wetter mit JSON Antwort: http://api.openweathermap.org/data/2.5/forecast/daily?q=Berlin&mode=json&units=metric&cnt=1&lang=de local $Json1 = '{"cod":"200","message":0.0268,"city":{"id":2950159,"name":"Berlin","coord":{"lon":13.41053,"lat":52.524368},"country":"DE","population":1000000},"cnt":1,"list":[{"dt":1368874800,"temp":{"day":13.22,"min":10.57,"max":13.22,"night":10.57,"eve":12.49,"morn":13.22},"pressure":1015.76,"humidity":100,"weather":[{"id":501,"main":"Rain","description":"mäßiger Regen","icon":"10"}],"speed":7.08,"deg":269,"clouds":92,"rain":7.75}]}' Local $objJson = Jsmn_Decode($Json1) If (Jsmn_IsObject($objJson)) Then     ConsoleWrite('Data1:' & Jsmn_ObjGetKeys($objJson) & @CRLF)     ConsoleWrite('Data2:' & Jsmn_ObjGet($objJson, "day") & @CRLF)     ConsoleWrite('Data3:' & Jsmn_ObjGetCount($objJson) & @CRLF)     ConsoleWrite('Data4:' & Jsmn_ObjExists($objJson, "day") & @CRLF) Else     ConsoleWrite("Kein Objekt") EndIf Local $objJson2 = Jsmn_Decode($objJson)

Console:

Data1: Data2: Data3:6 Data4:True

Because this is my first contact with JSON I think that I am doing something wrong with the the function "Jsmn_ObjGet". But what am I doing wrong?

 

Regards

mmscripting



#7 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 18 May 2013 - 08:07 PM

Try this:

#include <JSMN.au3> ; Anfrage von Wetter mit JSON Antwort: http://api.openweathermap.org/data/2.5/forecast/daily?q=Berlin&mode=json&units=metric&cnt=1&lang=de local $Json1 = '{"cod":"200","message":0.0268,"city":{"id":2950159,"name":"Berlin","coord":{"lon":13.41053,"lat":52.524368},"country":"DE","population":1000000},"cnt":1,"list":[{"dt":1368874800,"temp":{"day":13.22,"min":10.57,"max":13.22,"night":10.57,"eve":12.49,"morn":13.22},"pressure":1015.76,"humidity":100,"weather":[{"id":501,"main":"Rain","description":"mäßiger Regen","icon":"10"}],"speed":7.08,"deg":269,"clouds":92,"rain":7.75}]}' Local $objJson = Jsmn_Decode($Json1) Local $List = Jsmn_ObjGet($objJson, "list") ; "list" is an array Local $objJson2 = $List[0] Local $Temp = Jsmn_ObjGet($objJson2, "temp") ; "temp" is an object ConsoleWrite(Jsmn_ObjGet($Temp, "day") & @LF) ConsoleWrite(Jsmn_ObjGet($Temp, "min") & @LF)


#8 mmscripting

mmscripting

    Seeker

  • Normal Members
  • 2 posts

Posted 19 May 2013 - 05:20 AM

Hi Ward,

 

ahh ok now I understand: You have do know about the JSON structure and like an encapsulation take each part in an own list / array and at some point there is only a string left which can be queried for or values.

Before I thought that I can just query the whole JSON object and the function will give me the value in return. Is this also in other programming languages a common behaviour to "encapsulate" the whole JSON object or maybe is there a way to query the whole object for the needed string?

 

Anyway: Thanks for your very fast and efficient help :thumbsup: !

 

Regards

mmscripting



#9 guinness

guinness

    all-consuming swarm in inconspicuous disguise

  • Developers
  • 17,235 posts

Posted 19 May 2013 - 10:06 AM

Funny that I need such a UDF now. This has to be the best JSON UDF I've seen floating around the Forums. Thanks ward.


Example List: _AdapterConnections()_AlwaysRun()_AppMon()_AppMonEx()_BinaryBin()_CheckMsgBox()_CmdLineRaw()_ContextMenu()_ConvertLHWebColor()/_ConvertSHWebColor()_DesktopDimensions()_DisplayPassword()_Fibonacci()_FileCompare()_FileCompareContents()_FileNameByHandle()_FilePrefix/SRE()_FindInFile()_GetBackgroundColor()/_SetBackgroundColor()_GetConrolID()_GetCtrlClass()_GetDirectoryFormat()_GetDriveMediaType()_GetFilename()/_GetFilenameExt()_GetHardwareID()_GetIP()_GetIP_Country()_GetOSLanguage()_GetSavedSource()_GetStringSize()_GetSystemPaths()_GetURLImage()_GIFImage()_GoogleWeather()_GUICtrlCreateGroup()_GUICtrlListBox_CreateArray()_GUICtrlListView_CreateArray()_GUICtrlListView_SaveCSV()_GUICtrlListView_SaveHTML()_GUICtrlListView_SaveTxt()_GUICtrlListView_SaveXML()_GUICtrlMenu_Recent()_GUICtrlMenu_SetItemImage()_GUICtrlTreeView_CreateArray()_GUIDisable()_GUIImageList_SetIconFromHandle()_GUIRegisterMsg()_GUISetIcon()_Icon_Clear()/_Icon_Set()_IdleTime()_InetGet()_InetGetGUI()_InetGetProgress()_IPDetails()_IsFileOlder()_IsGUID()_IsHex()_IsPalindrome()_IsRegKey()_IsStringRegExp()_IsSystemDrive()_IsUPX()_IsValidType()_IsWebColor()_Language()_Log()_MicrosoftInternetConnectivity()_MSDNDataType()_PathFull/GetRelative/Split()_PathSplitEx()_PrintFromArray()_ProgressSetMarquee()_ReDim()_RockPaperScissors()/_RockPaperScissorsLizardSpock()_ScrollingCredits_SelfDelete()_SelfRename()_SelfUpdate()_SendTo()_ShellAll()_ShellFile()_ShellFolder()_SingletonHWID()_SingletonPID()_Startup()_StringCompact()_StringIsValid()_StringRegExpMetaCharacters()_StringReplaceWholeWord()_StringStripChars()_Temperature()_TrialPeriod()_UKToUSDate()/_USToUKDate()_WinAPI_Create_CTL_CODE()_WinAPI_CreateGUID()_WMIDateStringToDate()/_DateToWMIDateString()Au3 script parsingAutoIt SearchAutoIt3 PortableAutoIt3WrapperToPragmaAutoItWinGetTitle()/AutoItWinSetTitle()CodingDirToHTML5FileInstallrGeoIP databaseGUI - Only Close ButtonGUI ExamplesGUICtrlDeleteImage()GUICtrlGetBkColor()GUICtrlGetStyle()GUIEventsGUIGetBkColor()LockFile()Mapping CtrlIDsParseHeadersToSciTE()PasswordValidPasteBinPosts Per DayPreExpandQueue()Resource UpdateResourcesExSciTE JumpSettings INISHELLHOOKShunting-YardSignature CreatorStack()Stopwatch()StringAddLF()/StringStripLF()StringEOLToCRLF()VSCROLLWM_COPYDATAMore Examples...
Updated: 30/07/2014


#10 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 23 May 2013 - 08:02 AM

I wrote a helper function to get specific element in nested array/object returned from Jsmn_Decode() more easily.

 

Here is the code and example:

AutoIt         
#Include "JSMN.au3" Local $Json = '{"key1" : "value1", "key2" : [true, ["a", "b"], {"key3", "obj_in_array"}]}' Local $Obj = Jsmn_Decode($Json) Jsmn_Get_ShowResult($Obj, '["key1"]') Jsmn_Get_ShowResult($Obj, '["key2"][0]') Jsmn_Get_ShowResult($Obj, '["key2"][1][0]') Jsmn_Get_ShowResult($Obj, '["key2"][2]["key3"]') Local $Json = '{"strange key []" : "uses \\uXXXX for unsupported char in the key", "key_nospace_1" : {"key_nospace_2" : "don''t need \" if the key has no space"} }' Local $Obj = Jsmn_Decode($Json) Jsmn_Get_ShowResult($Obj, '["strange key [\u005D"]') Jsmn_Get_ShowResult($Obj, '[key_nospace_1][key_nospace_2]') Jsmn_Get_ShowResult($Obj, '[error1]') Jsmn_Get_ShowResult($Obj, '[error2') Func Jsmn_Get_ShowResult($Var, $Key)     Local $Ret = Jsmn_Get($Var, $Key)     If @Error Then         Switch @error             Case 1                 ConsoleWrite("Error 1: key not exists" & @LF)             Case 2                 ConsoleWrite("Error 2: syntax error" & @LF)         EndSwitch     Else         ConsoleWrite($Key & " => " & VarGetType($Ret) & ": " & $Ret & @LF)     EndIf EndFunc Func Jsmn_Get($Var, $Key)     If Not $Key Then Return $Var     Local $Match = StringRegExp($Key, "(^\[([^\]]+)\])", 3)     If IsArray($Match) Then         Local $Index = Jsmn_Decode($Match[1])         $Key = StringTrimLeft($Key, StringLen($Match[0]))         If IsString($Index) And Jsmn_IsObject($Var) And Jsmn_ObjExists($Var, $Index) Then             Local $Ret = Jsmn_Get(Jsmn_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 = Jsmn_Get($Var[$Index], $Key)             Return SetError(@Error, 0, $Ret)         Else             Return SetError(1, 0, "")         EndIf     EndIf     Return SetError(2, 0, "") EndFunc

  • Danyfirex and Siahtech like this

#11 Fly By Night

Fly By Night

    Seeker

  • Active Members
  • 16 posts

Posted 27 May 2013 - 04:59 PM

I'm trying to parse the data below,

 

{"events":[{"world_id":2012,"map_id":873,"event_id":"659149D4-43EC-4DCB-A6BB-0B2D402B537B","state":"Warmup"},
{"world_id":2012,"map_id":873,"event_id":"72F93CD8-94AC-4234-8D86-996CCAC76A46","state":"Warmup"},
{"world_id":2012,"map_id":873,"event_id":"81F89AC2-4764-49CB-A4F1-8B7546201DC7","state":"Active"},
{"world_id":2012,"map_id":873,"event_id":"FF71DE90-423B-4685-A343-83487A330C7A","state":"Active"}]}

The end result would be an array with 3 columns for map_id, event_id, and state filled with the relevant data.

 

By striping '{"events":[' and ']}' from either end i managed to get the values of the first line/object but how do extract further items? I can't work out how to loop into the next line(s).

 

Thanks for any help. My code isn't really worth showing hence no code example.

 

 



#12 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 28 May 2013 - 01:44 PM

Using Jsmn_Get(), it's easy.

I will put this function into the UDF later.

AutoIt         
#Include "JSMN.au3" Local $Json = '{"events":[{"world_id":2012,"map_id":873,"event_id":"659149D4-43EC-4DCB-A6BB-0B2D402B537B","state":"Warmup"},{"world_id":2012,"map_id":873,"event_id":"72F93CD8-94AC-4234-8D86-996CCAC76A46","state":"Warmup"},{"world_id":2012,"map_id":873,"event_id":"81F89AC2-4764-49CB-A4F1-8B7546201DC7","state":"Active"},{"world_id":2012,"map_id":873,"event_id":"FF71DE90-423B-4685-A343-83487A330C7A","state":"Active"}]}' Local $Obj = Jsmn_Decode($Json) ConsoleWrite(Jsmn_Get($Obj, '["events"][0]["world_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][0]["map_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][0]["event_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][0]["state"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][1]["world_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][1]["map_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][1]["event_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][1]["state"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][2]["world_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][2]["map_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][2]["event_id"]') & @LF) ConsoleWrite(Jsmn_Get($Obj, '["events"][2]["state"]') & @LF) Func Jsmn_Get($Var, $Key)     If Not $Key Then Return $Var     Local $Match = StringRegExp($Key, "(^\[([^\]]+)\])", 3)     If IsArray($Match) Then         Local $Index = Jsmn_Decode($Match[1])         $Key = StringTrimLeft($Key, StringLen($Match[0]))         If IsString($Index) And Jsmn_IsObject($Var) And Jsmn_ObjExists($Var, $Index) Then             Local $Ret = Jsmn_Get(Jsmn_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 = Jsmn_Get($Var[$Index], $Key)             Return SetError(@Error, 0, $Ret)         Else             Return SetError(1, 0, "")         EndIf     EndIf     Return SetError(2, 0, "") EndFunc


#13 Fly By Night

Fly By Night

    Seeker

  • Active Members
  • 16 posts

Posted 28 May 2013 - 08:04 PM

Thanks! Wow, it's easy to do once you know the correct syntax. Thanks again for the UDF and your code above really helps me understand it more now :)


Edited by Fly By Night, 28 May 2013 - 08:04 PM.


#14 JRSmile

JRSmile

    MCSE 2012R2

  • Active Members
  • PipPipPipPipPipPip
  • 461 posts

Posted 29 May 2013 - 10:30 AM

Perfect, right at the time i thought of buying the $400 n/Software json Activex the native and free udf comes around, thank you very much.
$a=StringSplit("547275737420796F757220546563686E6F6C75737421","")For $b=1 To UBound($a)+(-1*-1*-1)step(2^4/8);&$b+=1*2/40*µ&Asc(4)Assign("c",Eval("c")&Chr(Dec($a[$b]&$a[$b+1])))''Chr("a")&"HI"Next;time_U&r34d,ths,U-may=get$the&c.l.u.e;b3st-regards,JRSmile;MsgBox(0x000000,"",Eval("c"));PiEs:d0nt+*b3.s4d.4ft3r.1st-try:-)

#15 robertcollier4

robertcollier4

    Wayfarer

  • Active Members
  • Pip
  • 55 posts

Posted 29 May 2013 - 10:56 AM

Can it be possible to use put and get via JSON Dot Notation?



#16 Fly By Night

Fly By Night

    Seeker

  • Active Members
  • 16 posts

Posted 01 June 2013 - 11:46 AM

I'm searching through a long (>800) list looking for a specific value using the following code and my routine seems to be very slow compared to using a similar version just using _StringBetween on the raw JSON string.

 

I'm not sure if i can reduce the number of Jsmn_Get calls using the logic below. Is the While..Wend method below one to use or am i missing a better way to hunt for a value. Thanks again.

Func getEventstate($eventID) ; Search for a specific EventID and return it's state Local $evState = "", $evFound = 0 Local $i = 0     While ((Jsmn_Get($EventsObj, '["events"][' & $i & ']["event_id"]') <> "") And $evFound = 0)         If Jsmn_Get($EventsObj, '["events"][' & $i & ']["event_id"]') == $eventID Then             $evState = Jsmn_Get($EventsObj, '["events"][' & $i & ']["state"]')             $evFound = 1         EndIf         $i = $i + 1     WEnd     Return ($evState) EndFunc   ;==>getEventstate2

For reference, same using _StringBetween

Func getEventstate($eventID)     $srcStr = '"event_id":"' & $eventID & '","state":"'     $evState = _StringBetween($EventsJson, $srcStr, '"}')     If $evState <> "" Then         Return ($evState[0])     EndIf EndFunc   ;==>getEventstate


#17 Ward

Ward

    Prodigy

  • Active Members
  • PipPipPip
  • 160 posts

Posted 01 June 2013 - 06:20 PM

You can optimize this code (and maybe any code) to avoid do the same thing everytime in the loop. For example, you can get '["events"]' object and reuse it in the loop. And then you can aslo try to modify the code to get "[' & $i & ']" only once when the $i was changed.

 

However, even you wrote the optimized code, I don't think it will run faster than _StringBetween. So don't try to compare with that. 



#18 Fly By Night

Fly By Night

    Seeker

  • Active Members
  • 16 posts

Posted 01 June 2013 - 11:45 PM

If I wanted to load in the Events object and search through all the values of a key for a specific one, what method would you recommend? I wasn't sure how to do this optimally that is why i ended up just doing a While..Wend using the $i to increment onto the next key until i find the value i'm after. Also i wasn't sure if   '["events"][' & $i & ']["event_id"]') <> "" was the correct method to check for end of keys. I realise what i'm after is a Jsmn_Search function that looks for a value and returns how to obtain it via Jsmn_Get.

 

I did a test of copying key values into an array using Jsmn_Get then _StringBetween and Jsmn_Get was a lot faster on a test of about 1600 keys.


Edited by Fly By Night, 02 June 2013 - 11:36 AM.


#19 santaryan

santaryan

    Seeker

  • Normal Members
  • 6 posts

Posted 06 July 2013 - 12:03 AM

Here is the JSMN_Get function that Ward wrote in post #12 written to allow both delimiter notation (dot, comma, pipe, whatever notation) and JSON notation.

AutoIt         
; #FUNCTION# =================================================================== ; Name:             Jsmn_Get ; Description:    Retrieves a value from the scripting.dictionary key specified ; ; Parameter(s): ;                       $Object     Scripting dictionary object ; ;                       $Key        Key for the value you want to retrieve. Follows delimiter based or JSON based format depending on the notation parameter ; ;                       $Notation ;                                       0 - Delimiter based - Key1.Key2 ;                                       1 - JSON format based ["key1"]["key2"] ; ;                       $Delim      If notation = 0 Then this is the delimiter between keys ; ; Requirement(s):   JSMN JSON library by Ward. ; ; Return Value(s): ;                           On Success  Returns value from key ; ;                           On Failure      Returns a blank string and an error value. ;                                               @error ;                                                   1 - key does not exist ;                                                   2 - notation syntax error ;                                               @Extended - Only used if notation = 0 ;                                                   See StringSplit error codes ; ; Author(s):    Ward (modified slightly by SantaRyan to add delimiter notation) ;=============================================================================== Func Jsmn_Get($Object, $Key, $Notation = 0, $Delim = ".")     If Not $Key Then Return $Object     If IsKeyword($Notation) Then $Notation = 0     Local $Index, $Match, $Ret     If $Notation = 0 Then         $Match = StringSplit($Key, $Delim, 1)         If IsArray($Match) Then             $Index = $Match[1]             $Key = ""             For $i = 2 To $Match[0]                 $Key &= $Match[$i] & $Delim             Next             $Key = StringTrimRight($Key, 1)         Else             Return SetError(2, 0, "")         EndIf     ElseIf $Notation = 1 Then         $Match = StringRegExp($Key, "(^\[([^\]]+)\])", 3)         If IsArray($Match) Then             $Index = Jsmn_Decode($Match[1])             $Key = StringTrimLeft($Key, StringLen($Match[0]))         Else             Return SetError(2, 0, "")         EndIf     EndIf     If IsString($Index) And Jsmn_IsObject($Object) And Jsmn_ObjExists($Object, $Index) Then         $Ret = Jsmn_Get(Jsmn_ObjGet($Object, $Index), $Key, $Delim)         Return SetError(@error, 0, $Ret)     ElseIf IsNumber($Index) And IsArray($Object) And $Index >= 0 And $Index < UBound($Object) Then         $Ret = Jsmn_Get($Object[$Index], $Key, $Delim)         Return SetError(@error, 0, $Ret)     Else         Return SetError(1, 0, "")     EndIf EndFunc   ;==>Jsmn_Get

Edited by santaryan, 06 July 2013 - 12:16 AM.


#20 level20peon

level20peon

    Seeker

  • Active Members
  • 41 posts

Posted 03 November 2013 - 04:39 PM

Hello,

 

thanks for this UDF, it has been really helpful so far. However, I got one JSON string that seems impossible to decode with this UDF (I validated it with a JSON validator, so does not seem to be invalid):

 

I doesn't work in this form:

#Include <JSMN.au3> $JSON='{"a":{"742597609":{"v":673,"p":4.91,"lb":"test1","av":58,"sl":11.6},"534165346":{"v":777,"p":5.66,"lb":"test2","av":58,"sl":13.3}},"b":16092018}' Local $objJson = Jsmn_Decode($JSON) If Jsmn_IsObject($objJson) Then     Local $auctions = Jsmn_ObjGet($objJson, "a")     For $i = 0 To UBound($auctions)-1         Local $objJson2 = $auctions[$i]         ConsoleWrite(Jsmn_ObjGet($objJson2, "p"))     Next EndIf

When I transform the JSON string like this, it produces an output:

#Include <JSMN.au3> $JSON='{"a":{"742597609":{"v":673,"p":4.91,"lb":"test1","av":58,"sl":11.6},"534165346":{"v":777,"p":5.66,"lb":"test2","av":58,"sl":13.3}},"b":16092018}' ;JSMN compatibility $JSON=StringRegExpReplace($JSON,'"\d{9}":','') $JSON=StringReplace($JSON,'{"a":{','{"a":[') $JSON=StringReplace($JSON,'}},','}],') ;produces ;$JSON='{"a":[{"v":673,"p":4.91,"lb":"test1","av":58,"sl":11.6},{"v":777,"p":5.66,"lb":"test2","av":58,"sl":13.3}],"b":16092018}' Local $objJson = Jsmn_Decode($JSON) If Jsmn_IsObject($objJson) Then     Local $auctions = Jsmn_ObjGet($objJson, "a")     For $i = 0 To UBound($auctions)-1         Local $objJson2 = $auctions[$i]         ConsoleWrite(Jsmn_ObjGet($objJson2, "p"))     Next EndIf

What am I doing wrong ? Is it possible to get the auction details (like "p" in this example) without transforming the JSON string ?


Edited by level20peon, 03 November 2013 - 04:41 PM.






Also tagged with one or more of these keywords: JSON, JSMN

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users