json.append() and empty properties

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

Post Reply
bmceldowney
Kobold
Posts: 8
Joined: Mon Jun 28, 2010 9:35 pm

json.append() and empty properties

Post 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?

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: json.append() and empty properties

Post 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.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

prestidigitator
Dragon
Posts: 317
Joined: Fri Apr 23, 2010 8:17 pm

Re: json.append() and empty properties

Post 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. :(
"He knows not how to know who knows not also how to un-know." --Sir Richard Burton

User avatar
aliasmask
Deity
Posts: 8624
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: json.append() and empty properties

Post 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].

prestidigitator
Dragon
Posts: 317
Joined: Fri Apr 23, 2010 8:17 pm

Re: json.append() and empty properties

Post 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).
Last edited by prestidigitator on Wed Jun 30, 2010 7:30 pm, edited 4 times in total.
"He knows not how to know who knows not also how to un-know." --Sir Richard Burton

User avatar
Azhrei
Site Admin
Posts: 12058
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: json.append() and empty properties

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

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: json.append() and empty properties

Post 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.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

User avatar
Raoden
Dragon
Posts: 381
Joined: Fri Dec 18, 2009 2:33 am
Location: San Diego

Re: json.append() and empty properties

Post 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).
"Fairy tales do not tell children the dragons exist. Children already know that dragons exist. Fairy tales tell children the dragons can be killed."
- G. K. Chesterton

Wonderful HTML/CSS reference * Color Manager * Token Manager 2.0

Post Reply

Return to “Macros”