├── .gitignore ├── JSON_FromObj.ahk ├── JSON_ToObj.ahk ├── LICENSE.TXT └── README.TXT /.gitignore: -------------------------------------------------------------------------------- 1 | *.lnk -------------------------------------------------------------------------------- /JSON_FromObj.ahk: -------------------------------------------------------------------------------- 1 | ; Copyright © 2013 VxE. All rights reserved. 2 | 3 | ; Serialize an object as JSON-like text OR format a string for inclusion therein. 4 | ; NOTE: scientific notation is treated as a string and hexadecimal as a number. 5 | ; NOTE: UTF-8 sequences are encoded as-is, NOT as their intended codepoint. 6 | json_fromobj( obj ) { 7 | 8 | If IsObject( obj ) 9 | { 10 | isarray := 0 ; an empty object could be an array... but it ain't, says I 11 | for key in obj 12 | if ( key != ++isarray ) 13 | { 14 | isarray := 0 15 | Break 16 | } 17 | 18 | for key, val in obj 19 | str .= ( A_Index = 1 ? "" : "," ) ( isarray ? "" : json_fromObj( key ) ":" ) json_fromObj( val ) 20 | 21 | return isarray ? "[" str "]" : "{" str "}" 22 | } 23 | else if obj IS NUMBER 24 | return obj 25 | ; else if obj IN null,true,false ; AutoHotkey does not natively distinguish these 26 | ; return obj 27 | 28 | ; Encode control characters, starting with backslash. 29 | StringReplace, obj, obj, \, \\, A 30 | StringReplace, obj, obj, % Chr(08), \b, A 31 | StringReplace, obj, obj, % A_Tab, \t, A 32 | StringReplace, obj, obj, `n, \n, A 33 | StringReplace, obj, obj, % Chr(12), \f, A 34 | StringReplace, obj, obj, `r, \r, A 35 | StringReplace, obj, obj, ", \", A 36 | StringReplace, obj, obj, /, \/, A 37 | While RegexMatch( obj, "[^\x20-\x7e]", key ) 38 | { 39 | str := Asc( key ) 40 | val := "\u" . Chr( ( ( str >> 12 ) & 15 ) + ( ( ( str >> 12 ) & 15 ) < 10 ? 48 : 55 ) ) 41 | . Chr( ( ( str >> 8 ) & 15 ) + ( ( ( str >> 8 ) & 15 ) < 10 ? 48 : 55 ) ) 42 | . Chr( ( ( str >> 4 ) & 15 ) + ( ( ( str >> 4 ) & 15 ) < 10 ? 48 : 55 ) ) 43 | . Chr( ( str & 15 ) + ( ( str & 15 ) < 10 ? 48 : 55 ) ) 44 | StringReplace, obj, obj, % key, % val, A 45 | } 46 | return """" obj """" 47 | } ; json_fromobj( obj ) -------------------------------------------------------------------------------- /JSON_ToObj.ahk: -------------------------------------------------------------------------------- 1 | ; Copyright © 2013 VxE. All rights reserved. 2 | 3 | ; Uses a two-pass iterative approach to deserialize a json string 4 | json_toobj( str ) { 5 | 6 | quot := """" ; firmcoded specifically for readability. Hardcode for (minor) performance gain 7 | ws := "`t`n`r " Chr(160) ; whitespace plus NBSP. This gets trimmed from the markup 8 | obj := {} ; dummy object 9 | objs := [] ; stack 10 | keys := [] ; stack 11 | isarrays := [] ; stack 12 | literals := [] ; queue 13 | y := nest := 0 14 | 15 | ; First pass swaps out literal strings so we can parse the markup easily 16 | StringGetPos, z, str, %quot% ; initial seek 17 | while !ErrorLevel 18 | { 19 | ; Look for the non-literal quote that ends this string. Encode literal backslashes as '\u005C' because the 20 | ; '\u..' entities are decoded last and that prevents literal backslashes from borking normal characters 21 | StringGetPos, x, str, %quot%,, % z + 1 22 | while !ErrorLevel 23 | { 24 | StringMid, key, str, z + 2, x - z - 1 25 | StringReplace, key, key, \\, \u005C, A 26 | If SubStr( key, 0 ) != "\" 27 | Break 28 | StringGetPos, x, str, %quot%,, % x + 1 29 | } 30 | ; StringReplace, str, str, %quot%%t%%quot%, %quot% ; this might corrupt the string 31 | str := ( z ? SubStr( str, 1, z ) : "" ) quot SubStr( str, x + 2 ) ; this won't 32 | 33 | ; Decode entities 34 | StringReplace, key, key, \%quot%, %quot%, A 35 | StringReplace, key, key, \b, % Chr(08), A 36 | StringReplace, key, key, \t, % A_Tab, A 37 | StringReplace, key, key, \n, `n, A 38 | StringReplace, key, key, \f, % Chr(12), A 39 | StringReplace, key, key, \r, `r, A 40 | StringReplace, key, key, \/, /, A 41 | while y := InStr( key, "\u", 0, y + 1 ) 42 | if ( A_IsUnicode || Abs( "0x" SubStr( key, y + 2, 4 ) ) < 0x100 ) 43 | key := ( y = 1 ? "" : SubStr( key, 1, y - 1 ) ) Chr( "0x" SubStr( key, y + 2, 4 ) ) SubStr( key, y + 6 ) 44 | 45 | literals.insert(key) 46 | 47 | StringGetPos, z, str, %quot%,, % z + 1 ; seek 48 | } 49 | 50 | ; Second pass parses the markup and builds the object iteratively, swapping placeholders as they are encountered 51 | key := isarray := 1 52 | 53 | ; The outer loop splits the blob into paths at markers where nest level decreases 54 | Loop Parse, str, % "]}" 55 | { 56 | StringReplace, str, A_LoopField, [, [], A ; mark any array open-brackets 57 | 58 | ; This inner loop splits the path into segments at markers that signal nest level increases 59 | Loop Parse, str, % "[{" 60 | { 61 | ; The first segment might contain members that belong to the previous object 62 | ; Otherwise, push the previous object and key to their stacks and start a new object 63 | if ( A_Index != 1 ) 64 | { 65 | objs.insert( obj ) 66 | isarrays.insert( isarray ) 67 | keys.insert( key ) 68 | obj := {} 69 | isarray := key := Asc( A_LoopField ) = 93 70 | } 71 | 72 | ; arrrrays are made by pirates and they have index keys 73 | if ( isarray ) 74 | { 75 | Loop Parse, A_LoopField, `,, % ws "]" 76 | if ( A_LoopField != "" ) 77 | obj[key++] := A_LoopField = quot ? literals.remove(1) : A_LoopField 78 | } 79 | ; otherwise, parse the segment as key/value pairs 80 | else 81 | { 82 | Loop Parse, A_LoopField, `, 83 | Loop Parse, A_LoopField, :, % ws 84 | if ( A_Index = 1 ) 85 | key := A_LoopField = quot ? literals.remove(1) : A_LoopField 86 | else if ( A_Index = 2 && A_LoopField != "" ) 87 | obj[key] := A_LoopField = quot ? literals.remove(1) : A_LoopField 88 | } 89 | nest += A_Index > 1 90 | } ; Loop Parse, str, % "[{" 91 | 92 | If !--nest 93 | Break 94 | 95 | ; Insert the newly closed object into the one on top of the stack, then pop the stack 96 | pbj := obj 97 | obj := objs.remove() 98 | obj[key := keys.remove()] := pbj 99 | If ( isarray := isarrays.remove() ) 100 | key++ 101 | 102 | } ; Loop Parse, str, % "]}" 103 | 104 | Return obj 105 | } ; json_toobj( str ) -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jim-VxE/AHK-Lib-JSON_ToObj/52cd6dc73ded789d7b7181f979f514ca9bb9119d/LICENSE.TXT -------------------------------------------------------------------------------- /README.TXT: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jim-VxE/AHK-Lib-JSON_ToObj/52cd6dc73ded789d7b7181f979f514ca9bb9119d/README.TXT --------------------------------------------------------------------------------