Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

JSON read/write parser


  • Please log in to reply
32 replies to this topic
polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
I wrote this to help with automated hacking of my xulrunner/prism code. Others may also find it useful as a faster and more compact alternative to XML or INI.

; JSON string:
j = [color=brown]{"version":"1","window":{"state":3,"screenX":25,"screenY":25,"width":790,"height":605,"test":{"nested":"object"}},"sidebar":{"visible":false,"width":"200"}}[/color]
MsgBox, % json(j, "version") ; returns "1"
MsgBox, % json(j, "window.width", 800) ; returns 790, sets window->width to 800

r = [color=brown]{ "a" : true, "b" : [ 1, [ 2.1, 2.2, { "sub" : false, "test" : [ null, "pass" ] } ], 3 ] }[/color]
MsgBox, % json(r, "b[1][2].test[1]") ; array support

Download

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


heresy
  • Members
  • 291 posts
  • Last active: Sep 26 2008 10:47 PM
  • Joined: 11 Mar 2008
it reminds me Python's dictionary data type too. good for handling large variables.
i think it would be comfortable if we can skip first parameter and it creates a global variable automatically.
which would look like below
json("Screen.Width", A_ScreenWidth) ;set
MsgBox % ("Screen.Width") ;get
Thanks for sharing
Easy WinAPI - Dive into Windows API World
Benchmark your AutoHotkey skills at PlayAHK.com

n-l-i-d
  • Guests
  • Last active:
  • Joined: --
Impressive! 8)

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

i think it would be comfortable if we can skip first parameter and it creates a global variable automatically

Good idea, you can use static variable too. The main version will stay the same so you can modify external objects.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


haichen
  • Members
  • 200 posts
  • Last active: Oct 20 2013 01:14 PM
  • Joined: 05 Feb 2007
There is a problem with spaces after the :.
Would be nice if you (or someone else) could include this.
I tried to figure the problem and I think if there is Whitespace after : z is too small. But the RegexMatch with backreferences ist to complicated for me. :D

edit: Forgot to say: there's only a problem when changing values like ..."width": 790,...

Thanks for this nice piece of code.

Ice_Tea
  • Members
  • 131 posts
  • Last active: Aug 25 2010 11:11 AM
  • Joined: 12 Jan 2008
it seems to be a kickass function imho, think I'll use it

haichen
  • Members
  • 200 posts
  • Last active: Oct 20 2013 01:14 PM
  • Joined: 05 Feb 2007
I changed the function a little bit. Now it works with structured json files and with spaces after the :.
json(ByRef js, s, v = "") {
	j = %js%
	Loop, Parse, s, .
	{
		p = 2
		RegExMatch(A_LoopField, "([+\-]?)([^[]+)(?:\[(\d+)\])?", q), q3 := q3 ? q3 : 0
		Loop {
			If (!p := RegExMatch(j, "(""|')([^\1]+?)\1\s*:\s*((""|')?[^\4]*?\4|(\{(?:[^{}]*+|(?5))*\})|[+\-]?\d+(?:\.\d*)?|true|false|null?)\s*(?:,|$|\})", x, p))
				Return
			Else If (-1 == q3 -= x2 == q2 or q2 == "*") {
				j = %x3%
				z += p + StrLen(x2) - 2
				Break
			}
			Else p += StrLen(x)
		}
	}

 [color=red] z-=StrLen(x)
  If (v !="" ) {
    NewStr := RegExReplace(x, x3 , v)
    js := RegExReplace(js, x , NewStr,"",1,z)
  }[/color]

	Return, x3 == "false" ? 0 : x3 == "true" ? 1 : x3 == "null" or x3 == "nul" ? "" : SubStr(x3, 1, 1) == "" ? SubStr(x3, 2, -1) : x3
}

Example:
j=
(
{
   "intl": {
      "app_locale": "de"
   },
   "options_window": {
      "last_tab_index": "2"

   }
}
)


test="1212"
MsgBox, % json(j,"options_window.last_tab_index", test) 
MsgBox, % json(j,"options_window.last_tab_index")


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

I changed the function a little bit. Now it works with structured json files and with spaces after the :.

Thanks for writing a fix. This function is not standards compliant with array handling, the wildcard selector is also a custom extension. I had planned to release a fully conformant update but left AutoHotkey to use C# in my toolchain for which I wrote SimpleJSON. I'll look at the ahk code again tonight and will try to make the final adjustments.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
For those who are interested version 2a is what I currently use. It includes a few bug fixes and supports complex nested arrays:

j = { "a" : true, "b" : [ 1, [ 2.1, 2.2, { "sub" : false, "test" : [ null, "pass" ] } ], 3 ] }
MsgBox, % json(j, "b[1][2].test[1]")

The only problem is an odd quirk in pcre which causes a failure to match {"v":"1"} where "1" could be any one or two digit number, with or without quotes. I think this is due to conflicting atomic alternations; if anyone can write better regex please post them.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Rabiator
  • Members
  • 292 posts
  • Last active: Aug 29 2016 09:29 PM
  • Joined: 17 Apr 2005
Unfortunately the example in your first post doesn't work any more.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

Unfortunately the example in your first post doesn't work any more.

Yes, v2 has a bug I previously mentioned - notice "version":"1" in the example and "last_tab_index": ""1212"" haichens code.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

The only problem is an odd quirk in pcre which causes a failure to match {"v":"1"} where "1" could be any one or two digit number, with or without quotes.

Has this been fixed? I see no such problem, but it does fail to match any quoted string containing the character 1. What is the purpose of the 1 in the highlighted character classes below?
If (!p := RegExMatch(j, "(?<!\\)(""|')([color=red][^\-1][/color]+?)(?<!\\)(?-1)\s*:\s*((\{(?:[^{}]++|(?-1))*\})|(\[(?:[^[\]]++|(?-1))*\])|"
				. "(?<!\\)(""|')[color=red][^\-1][/color]*?(?<!\\)(?-1)|[+\-]?\d+(?:\.\d*)?|true|false|null?)\s*(?:,|$|\})", x, p))


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

it does fail to match any quoted string containing the character 1.

After some testing I came to the same conclusion, perhaps I didn't check well enough when I made my earlier claim.

What is the purpose of the 1 in the highlighted character classes below? ... [^\-1]

\-1 is a relative reference to a captured subpattern - see SUBPATTERNS AS SUBROUTINES in pcre(3). I replaced the relative references with absolute ones to work around the bug and the parser works! Thanks for drawing my attention to this.

Version 2 has been committed to the SVN. The old version can be downloaded for historic reasons.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit


Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

\-1 is a relative reference to a captured subpattern

I believe that is incorrect.

BACKREFERENCES

\n reference by number (can be ambiguous)
\gn reference by number
\g{n} reference by number
\g{-n} relative reference by number
\k<name> reference by name (Perl)
\k'name' reference by name (Perl)
\g{name} reference by name (Perl)
\k{name} reference by name (.NET)
(?P=name) reference by name (Python)

Anyway, thanks for the prompt update.

Another thing... should ' be equivalent to "?
[*:3kg6v03v]"string' is valid, but doesn't seem like it should be. Using \g{-1} instead of (?-1) would invalidate it.
[*:3kg6v03v]Using "string", both " are omitted from the return value.
[*:3kg6v03v]Using 'string', both ' are included in the return value.
[*:3kg6v03v]Using "string', both are omitted.
[*:3kg6v03v]Using 'string", both are kept.

polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012

\g{-n} relative reference by number

I must have missed that when converting (?-1) to a character class.

should ' be equivalent to "?

No, JSON doesn't define ' as a string token, only ". This is just a quirk in my parser - (""|') isn't matched up with SubStr(j, 1, 1) == """" on line 55 for performance reasons.

autohotkey.com/net Site Manager

 

Contact me by email (polyethene at autohotkey.net) or message tidbit