JSON 2.0 (and Jxon) - JSON lib for AutoHotkey

Post your working scripts, libraries and tools for AHK v1.1 and older
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: JSON / JSON2 [ AHK v.1.1+ and AHK v2.0-a049 ]

06 Sep 2014, 01:08

Updated OP to reflect recent changes.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

17 Feb 2015, 06:25

Updated OP based on some recent internal changes. The main feature of the update is the better exception handling. See Exception Handling in OP.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

01 Sep 2015, 10:24

GeekDude wrote:Using this library here!
That's great! :)
Coco-guest

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

05 Sep 2015, 15:09

evilC wrote:Any ideas on why this does not work?

Code: Select all

mc := new MyClass()

class MyClass{
	__New(){
		MsgBox % this.JSON.Dump({a: "hello"})
	}
	#Include JSON.ahk
}
I found the culprit, the recursive calls(specifically lines #194 & #196) uses JSON.Dump() instead of this.Dump(). Since it's a nested class, JSON in this case is not a global variable. I'll push the fix later today.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

06 Sep 2015, 09:45

@evilC: I've pushed a new branch with the recent changes. Apparently, providing support for allowing usage as nested class(via #Include) is a little bit tricky and presents several potential issues:
  • Nested class(es) such as JSON.Object and JSON.Array will not be able to reference the parent class unless the parent class passes a reference to itself through some means(e.g.: via __New or others). Neither will they be able to inherit from sibling class(es) since class Nested extends JSON.Sibling is no longer valid as JSON.Sibling is now YourClass.JSON.Sibling
  • Functions(if any were to be added) will not be callable since they are now YourClass.SomeFunction()
In short, putting effort to support this flexibility is not really ideal as it limits any development room left. Not that there are any further additions needed but I rather prefer better maintainability and extensibility. In the future, I suggest that you do the following instead:

Code: Select all

mc := new MyClass()

#Include JSON.ahk
class MyClass{
	__New(){
		MsgBox % this.JSON.Dump({a: "hello"})
	}
	
	class JSON extends JSON {
	}
}
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

07 Sep 2015, 14:31

Yeah, no biggie.
I don't mind JSON polluting the global namespace anyway, as it is a command that is so universal and should be part of AHK anyway.
Thanks for looking into it though.

Am loving this lib - using it for a lot of stuff lately.
Just tried using it to dump a class instance, and it seems fine - no trace of Function names or anything in there, even with inherited classes.
How hard would it be to include an option for Dump() to strip all properties that start with an underscore?
That way,you could have classes with loads of unimportant values _underscore prefixed, and when the library dumps the class, they get stripped out - leaving you with just the properties you wish to preserve.
Yeah, I could do that myself, but if your code already iterates through the object, it would save another pass.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

07 Sep 2015, 14:54

Sounda a little out of scope/unnecessary for most people to me. If you want to do it yourself I'd imagine it'd be pretty simple.
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

12 Sep 2015, 20:29

I used this library to implement persistent and per-profile settings in my ProfileHandler library.
This massively simplified my code over using IniRead / IniWrite - I could just dump the whole settings object to file :)
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

13 Oct 2015, 13:09

Do you have any plans for a reserved keyword error or warning? For example, when I try to deserialize {"_NewEnum": "Dummy"} to a normal array prototype I might expect it to error instead of giving me a borderline unloopable array. Something like that would reduce the risk of a targeted (or even accidental) attack on my scripts being able to break things.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

14 Oct 2015, 21:33

I've been working on a safer array prototype that should negate any reserved keyword issues, and it seems to work. Am I missing anything obvious? Other than that it won't work for keys that don't cast to string, such as object keys (which wouldn't be used with JSON anyways).

Code: Select all

#NoEnv
SetBatchLines, -1

y := new SafeObj

y.__Set := "Setter"
y.base := "Proto"
y.__Get := "Getter"
y._NewEnum := "Enumerator"
y.__Call := "Caller"

for k, v in y
	MsgBox, %k%: %v%

class SafeObj
{
	static Table := {}
	
	__New()
	{
		SafeObj.Table[this] := {Data: {}, Enum: ""}
	}
	
	__Set(k, v)
	{
		return SafeObj.Table[this].Data["|" k] := v
	}
	
	_NewEnum()
	{
		SafeObj.Table[this].Enum := SafeObj.Table[this].Data._NewEnum()
		return this
	}
	
	Next(ByRef k, ByRef v)
	{
		val := SafeObj.Table[this].Enum.Next(k, v), k := SubStr(k, 2)
		return val
	}
	
	__Get(k)
	{
		if (k != "_NewEnum")
			return SafeObj.Table[this].Data["|" k]
	}
}
Coco-guest

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

14 Oct 2015, 21:53

@GeekDude: My apologies, I can't really put a full response at the moment. A more flexible alternative that I was thinking is to actually mimic Javascript's JSON.parse() and JSON.stringify() by providing a reviver(for parsing) and a replacer(for stringify) parameters. It will allow the caller to whitelist/blacklist(or even return a custom value) properties of their choosing rather than just blacklisting a handful of constants.
geek
Posts: 1052
Joined: 02 Oct 2013, 22:13
Location: GeekDude
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

14 Oct 2015, 22:18

I'm not really familiar with how javascript's system, but my solution doesn't just blacklist a handful of constants. It adds a pipe to the beginning of every dict key so that you don't have to worry about a collision with a reserved keyword (assuming Lex doesn't go crazy and add a reserved keyword starting with pipe). It doesn't block the keys from being used, it lets you use them safely.
Coco-guest

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

14 Oct 2015, 23:04

JSON.parse() wrote:If a reviver is specified, the value computed by parsing is transformed before being returned. Specifically, the computed value, and all its properties (beginning with the most nested properties and proceeding to the original value itself), are individually run through the reviver, which is called with the object containing the property being processed as this and with the property name as a string and the property value as arguments. If the reviver function returns undefined (or returns no value, e.g. if execution falls off the end of the function), the property is deleted from the object. Otherwise the property is redefined to be the return value.

Code: Select all

reviver(this, key, value) { ; the object containing the property is passed as 'this'
	if (key ~= "^__((G|S)et|New|Call|Delete)$")
		return get_meta_func_ref(SomeClass, key, value)
	else if (key = some_reserved_key)
		throw Exception(msg, args*)
	return value ; return unchanged
}

Json_Load(text, Func("reviver"))
JSON.stringify() wrote:The replacer parameter can be either a function or an array. As a function, it takes two parameters, the key and the value being stringified. The object in which the key was found is provided as the replacer's this parameter. Initially it gets called with an empty key representing the object being stringified, and it then gets called for each property on the object or array being stringified. It should return the value that should be added to the JSON string, as follows:
  • If you return a Number, the string corresponding to that number is used as the value for the property when added to the JSON string.
  • If you return a String, that string is used as the property's value when adding it to the JSON string.
  • If you return a Boolean, "true" or "false" is used as the property's value, as appropriate, when adding it to the JSON string.
  • If you return any other object, the object is recursively stringified into the JSON string, calling the replacer function on each property, unless the object is a function, in which case nothing is added to the JSON string.
  • If you return undefined, the property is not included in the output JSON string.

Code: Select all

replacer(this, key, value) {
	if (IsObject(value) && IsFunc(value)) ; method
		return your_custom_serialization(value)
	return value ; do nothing
}

Json_Dump(value, Func("replacer"), indent)
I believe that this provides more flexibility and more control on how an object or JSON string is serialized/de-serialized. What do you think? I am also considering lexikos' comment(in another thread, can't link right now) about how an array is supposed to be stringified as per ECMAScript JSON object/array specification.
lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: JSON / Jxon [ AHK v.1.1.17.00+ and AHK v2.0-a058 ]

18 Oct 2015, 07:09

Coco wrote:class JSON extends JSON {
}
static JSON := JSON would be more efficient.
GeekDude wrote:SafeObj.Table[this] := ...
Ironically, this is not safe.
  1. this contains a reference to SafeObj (= this.base).
  2. SafeObj contains a reference to the Table object.
  3. The Table object contains a reference to this (as a key).
  4. Goto 1.
The objects will never be freed.

This should be sufficient:

Code: Select all

#NoEnv
SetBatchLines, -1

y := new SafeObj
 
y.__Set := "Setter"
y.base := "Proto"
y.__Get := "Getter"
y._NewEnum := "Enumerator"
y.__Call := "Caller"
 
for k, v in y
    MsgBox, %k%: %v%

class SafeObj
{
    static Key := {}  ; Unique key for internal data.

    __New()
    {
        ObjRawSet(this, SafeObj.Key, {})
    }

    __Set(k, v)
    {
        ObjRawSet(this[SafeObj.Key], k, v)
        return v
    }

    _NewEnum()
    {
        return ObjNewEnum(this[SafeObj.Key])
    }

    __Get(k)
    {
        return this[SafeObj.Key, k]
    }
}
It should work with any type of key and any string. There are no reserved names. You cannot get the base of the object, and it does not visibly inherit any properties from its base (e.g. y.Key does not return the key object). You should either avoid using methods such as InsertAt, or override them to call the appropriate function, as with _NewEnum => ObjNewEnum.
Coco
Posts: 771
Joined: 29 Sep 2013, 20:37
Contact:

Re: JSON 2.0 (and Jxon) - JSON lib for AutoHotkey

07 Nov 2015, 06:56

Updated to v2.0, main post has been updated to reflect changes. Changed interface to mimic JavaScript's JSON.parse() and JSON.stringify(). This update affects the class version only.

Changes:
  • Removed backward support for old Parse() and Stringify() methods
  • Removed JSON.Object and JSON.Array objects
  • Removed JSON.Load()'s jsonize paramter
  • Added reviver parameter to JSON.Load()
  • Added replacer parameter to JSON.Dump()
  • Improved handling of array(s) during stringification process
  • Limit indentation(pretty-printing) to 10 characters
  • Added missing error message
  • Disable / escaping when stringifying
User avatar
evilC
Posts: 4823
Joined: 27 Feb 2014, 12:30

Re: JSON 2.0 (and Jxon) - JSON lib for AutoHotkey

09 Nov 2015, 15:32

Coco, I submitted a pull request to your repo with a small fix that makes it compatible with AHK_H.
AHK_H supports a null type, so I just renamed your null var to _null

In regards to the new stringify changes, two thumbs up - way up!!

I haven't totally got my head around usage of the replacer, but it appears to satisfy my earlier request for a way of filtering out certain keys - and appears to be capable of a whole lot more.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: makdc96 and 155 guests