Jump to content


Photo

JSON read/write parser


  • Please log in to reply
26 replies to this topic

#1 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 11 August 2008 - 02:15 PM

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

#2 heresy

heresy
  • Members
  • 291 posts

Posted 11 August 2008 - 03:51 PM

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

#3 n-l-i-d

n-l-i-d
  • Guests

Posted 11 August 2008 - 04:38 PM

Impressive! 8)

#4 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 12 August 2008 - 11:10 AM

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.

#5 haichen

haichen
  • Members
  • 198 posts

Posted 10 September 2008 - 04:33 PM

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.

#6 Ice_Tea

Ice_Tea
  • Members
  • 131 posts

Posted 10 September 2008 - 08:50 PM

it seems to be a kickass function imho, think I'll use it

#7 haichen

haichen
  • Members
  • 198 posts

Posted 11 September 2008 - 12:45 AM

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")


#8 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 11 September 2008 - 10:35 AM

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.

#9 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 11 September 2008 - 09:46 PM

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.

#10 Rabiator

Rabiator
  • Members
  • 292 posts

Posted 11 September 2008 - 10:04 PM

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

#11 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 11 September 2008 - 10:55 PM

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.

#12 Lexikos

Lexikos
  • Administrators
  • 8918 posts

Posted 21 December 2008 - 10:33 AM

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))


#13 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 21 December 2008 - 11:16 AM

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.

#14 Lexikos

Lexikos
  • Administrators
  • 8918 posts

Posted 21 December 2008 - 01:06 PM

\-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.

#15 polyethene

polyethene

    Administrator

  • Administrators
  • 5474 posts

Posted 21 December 2008 - 02:01 PM

\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.