Page 1 of 1

json.append() and empty properties

Posted: Wed Jun 30, 2010 11:18 am
by bmceldowney
One last query (for now):

I want to store some [wiki]JSON Object[/wiki]s in a [wiki]JSON Array[/wiki] and store the array in a property. I want to be able to add and remove object from the [wiki]JSON Array[/wiki] whenever I like. I am trying to add items using this macro:

Code: Select all

[h:meleeWeaponList = meleeWeaponList()]
[h:weaponList = ""]

[h,foreach(element, meleeWeaponList):
  weaponList = listAppend(weaponList, element)]

[h:okay = input(
  "selection|" + weaponList + "|Choose a weapon to add|LIST|SELECT=0 VALUE=STRING"
)]

[h,if(okay == 1), code:{
  [h:weaponStats = json.get(meleeWeaponList, selection)]
  [h:weaponProperty = getProperty("Weapons", getSelected())]
  [h:weaponProperty = if(weaponProperty == "", "[]", weaponProperty)]
  [h:weaponProperty = json.append(weaponProperty, weaponStats)]
  [h:setProperty("Weapons", weaponProperty, getSelected())]
  [macro("[email protected]:WFRP"):""]
}]

The problem that I keep running into is that it is impossible for me to tell whether or not there is already a [wiki]JSON Array[/wiki] assigned to the property. [wfunc]json.type[/wfunc] won't tell me if weaponProperty contains a [wiki]JSON Array[/wiki] or not (returns UNKNOWN regardless of what the value of my property is), and if there is a [wiki]JSON Array[/wiki] already there then the [wfunc]if[/wfunc] statement throws an invalid condition error.

Is there a better mousetrap for this quandary?

Re: json.append() and empty properties

Posted: Wed Jun 30, 2010 1:01 pm
by biodude
bmceldowney wrote:...
The problem that I keep running into is that it is impossible for me to tell whether or not there is already a [wiki]JSON Array[/wiki] assigned to the property. [wfunc]json.type[/wfunc] won't tell me if weaponProperty contains a [wiki]JSON Array[/wiki] or not (returns UNKNOWN regardless of what the value of my property is), and if there is a [wiki]JSON Array[/wiki] already there then the [wfunc]if[/wfunc] statement throws an invalid condition error.

Is there a better mousetrap for this quandary?


I'm not exactly sure what the problem is, but I've never had a problem with json.type() not returning the correct type, especially after storing the property value via [wfunc]getProperty[/wfunc]. Have you tried to output the actual value that you are sending to json.type(), just to verify that it is, in fact a valid json as you expect? Sometimes weird things happen and if the format gets screwed up, json.type may no longer recognize it.

I also noticed you are passing [wfunc]getSelected[/wfunc] directly in the call to getProperty, which I see as being a little dangerous: getSelected can return a list if more than one token is selected ; You are probably better off processing getSelected() to ensure you are dealing with only one token, and maybe even switching token context ([wfunc]switchToken[/wfunc]) to avoid unexpected surprises.

Also, this is a commonly used check, which I never use anymore:

Code: Select all

[IF( variable=="" ):  code ]
because it goes haywire if the variable value is a json, due to some peculiarities of data types in MapTool:
  • JSONs are not strings (therefore you can not compare them to a string or similar data types: see [wfunc]json.equals[/wfunc] for methods to compare JSONs).
  • an empty string ("") also qualifies as an empty JSON (returns TRUE/1 if passed to [wfunc]json.isEmpty[/wfunc] ), but is of type "UNKNOWN" ([wfunc]json.type[/wfunc] returns "UNKNOWN").
  • JSONs are stored as strings in token properties (due to technicalities of the way property data is stored on a token): in terms of Macro Script, this is rarely an issue (more a performance issue owing to the need to convert JSONs to/from strings when writing/fetching from a token property).
  • JSONs can be easily converted to strings using the [wfunc]string[/wfunc] function.
Therefore, some alternatives to the above check include:

Code: Select all

[IF( string(variable)=="" ):  code ]  <!-- now you are always comparing a string to a string -->
[IF( json.type(variable)=="UNKNOWN" ):  code ]<!-- will catch empty strings, and anything not a valid json object or array. -->

Note I do not trust this kind of check:

Code: Select all

[IF( json.isEmpty(variable) ):  code ]
Because [wfunc]json.isEmpty[/wfunc] is not always reliable for this sort of thing: it will return 0 (not an empty json) for a string starting and ending with the right characters ("[]" or "{}"), even if the rest is not a valid JSON structure: e.g. "{foo}" returns 0 for json.isEmpty, but returns a NullPointerException error from json.type, or other json functions.

A few other points about data types in MapTool, in case anyone's interested:
  • numbers can be treated as strings, but strings aren't necessarily numbers.
  • numbers stored as, or extracted from strings often (but not always) revert to being numbers. For example, strformat( "%+d" , 4 ) produces a string ("+2"), but if you try to output that number, or concatenate it to other strings, all you get is "2".
    • strformat( " %+d" , 4 ) produces a string (" +2"), notice the leading space, which remains as " +2" on output, but:
    • qualifies as a number ([wfunc]isNumber[/wfunc] returns TRUE/1),
    • but throw errors if you try to do math with it.
    • It can easily be converted to a number, but comes out as a floating-point value with many decimal places (many of which are 0), unless you convert to an integer using [wfunc]floor[/wfunc]. ([wfunc]number[/wfunc]).

That was probably way more information that you wanted, but I felt like laying it all out there anyway.

Re: json.append() and empty properties

Posted: Wed Jun 30, 2010 1:31 pm
by prestidigitator
It's that kind of crap that makes me yearn for strongly typed languages.

So there are strings you can't pass to [wfunc]json.type[/wfunc]? I should hope that's a bug we can get fixed. If it doesn't parse completely as a JSON type, it should return "UNKNOWN" IMO, not throw a Java exception. :(

Re: json.append() and empty properties

Posted: Wed Jun 30, 2010 1:59 pm
by aliasmask
prestidigitator wrote:It's that kind of crap that makes me yearn for strongly typed languages.

So there are strings you can't pass to [wfunc]json.type[/wfunc]? I should hope that's a bug we can get fixed. If it doesn't parse completely as a JSON type, it should return "UNKNOWN" IMO, not throw a Java exception. :(

I think the only time it errors if the value is NULL... "" is okay. I use json.isEmpty for 90% of my checks. You can get Null for checks like this:

Code: Select all

[H: myVal = json.get(macro.args,0)]
When no arguments are passed or the macro button is clicked directly then I resort to this code:

Code: Select all

[H, if(json.isEmpty(macro.args)):myVal = "";myVal = json.get(macro.args,0)]

json.isEmpty('{foo}') is not empty because it's a string and IMO does the check correctly. json.isEmpty('[]') returns true, but json.isEmpty('[[]]') does not because is contains another array... albeit an empty array.

Although "" can be used to represent an empty array, you can't use if for functions like [wfunc]json.difference[/wfunc], [wfunc]json.union[/wfunc], [wfunc]json.merge[/wfunc] or [wfunc]json.intersection[/wfunc] where the "[]" is required or json.append("","").

As for the actual question, if json.type is not saying you have an array, then you probably don't have an array. My guess is MeleeWeaponList is a list "1,2,3" and not a json array '[1,2,3]'. But it's easy enough to convert a list to a json with [wfunc]json.fromList[/wfunc].

Re: json.append() and empty properties

Posted: Wed Jun 30, 2010 7:21 pm
by prestidigitator
Ah. Okay. I see. My usual is something like this:

Code: Select all

[h: array = getProperty("ArrayProperty")]
[h, if (json.type(array) != "ARRAY"): array = "[]"]
...

[h: obj = getProperty("ObjectProperty")]
[h, if (json.type(obj) != "OBJECT"): obj = "{}"]
...


IIRC it's been working for properties that are both empty and (hidden properties) not yet set (NULL).

Re: json.append() and empty properties

Posted: Wed Jun 30, 2010 7:22 pm
by Azhrei
biodude wrote:Because [wfunc]json.isEmpty[/wfunc] is not always reliable for this sort of thing: it will return 0 (not an empty json) for a string starting and ending with the right characters ("[]" or "{}"), even if the rest is not a valid JSON structure: e.g. "{foo}" returns 0 for json.isEmpty, but returns a NullPointerException error from json.type, or other json functions.

I hadn't heard of this before. I added it to my list...

Re: json.append() and empty properties

Posted: Thu Jul 01, 2010 8:23 am
by biodude
Here's another one Azhrei:
json.isEmpty("foo") also returns 0 (not empty), even though it's not even a json structure!

Another reason I don't rely on json.isEmpty to catch non-valid json's. json.type is really the most reliable at the moment.

Re: json.append() and empty properties

Posted: Thu Jul 01, 2010 2:36 pm
by Raoden
biodude, I think you're misreading what json.isEmpty is supposed to do. According to the wiki, it's supposed to return "1" for anything, JSON or not, that is "empty," (namely "", '[]', or '{}'), and return "0" for anything else (JSON or not).