Velocitip: Convert was-Boolean, now-String → Number, then enjoy $display.message

This tip is used in this post on sending tracked or untracked links based on each lead's “OK to track me” setting.

Boolean type fields enjoy a strange place in Marketo's Velocity implementation.

On the Marketo UI side and via most Marketo APIs, Booleans hold consistent true/false values (presumably using SQL BIT type fields under the hood).

But in Velocity, as I've explored before, a Boolean field on the Lead/Person is presented as one of two Strings:

  • "1" for Boolean true
  • "" (empty string) for Boolean false

That conversion is quite confusing in its own right (I don’t know of any other system that does that particular stringification) but even more confusing is how Velocity deals (or not) with it.

See, Velocity gets a lot of things from Java.

But in Java, there’s no such thing as a Boolean-ish value with truth-iness and false-iness qualities. There are only real Booleans. If you try to use a non-Boolean in a Boolean context, you get a fatal error.

Velocity strives to be an easier-to-use dialect of Java which throws fewer errors. Its way of avoiding this particular error is a simple rule: anything other than null or the real Boolean false is truthy. The empty String and any non-empty String are both truthy!

So if you do:

#if( $lead.someBooleanField )
Field is true
#else
Field is false
#end

you will always get “Field is true” even if you've unchecked the corresponding checkbox in the Marketo UI.

If Marketo Booleans were Velocity/Java Booleans, on the other hand, this would've worked fine. But alas.

So the question is what to do with these sort-of-Boolean-like-Strings (whose meaning we understand, but the VTL language doesn't). What do we convert them to to make them more usable?

You can do a step-by-step conversion to Boolean by doing a String comparison:

#if(  $lead.someBooleanField.equals("1") )
#set( $lead.someBooleanField == true )
#else
#set( $lead.someBooleanField == false )
#end

After that conversion, #if($field) will work as expected.

But I've been thinking lately that maybe converting those Strings to Numbers — 0 for false and 1 for true, as the standard goes — gives us more juice within Velocity than if we were to go with true Booleans.

See, you can do #if($field.equals(0)) or #if($field.equals(1))[1] to use these well-known Boolean-like Numbers, but having Numbers gives you access to something more special: the Velocity DisplayTool $display.message function.

$display.message is tricky at first because it uses Java's MessageFormat syntax. But buried inside that is the ChoiceFormat type — that's where the power comes in.

ChoiceFormat allows you to switch output based on numbers in an ascending set.

That is, using shorthand syntax, you can output some string if a Number variable is 0, or some other string if the Number is 1 (plus yet another string if the Number is 2, and so on, but that's not pertinent to Boolean-like Numbers).

Code speaks louder than words:

#set( $false_true = "{0,choice,0#false|1#true}" )
#set( $no_yes = "{0,choice,0#no|1#yes}" )
#set( $out_in = "{0,choice,0#out|1#in}" )
#set( $fudgeability = "{0,choice,0#may well be somethin''|1#ain''t nothin''}" 

You ${display.message($arenot_are, $isBusiness)} a business user.

You have opted ${display.message($out_in, $isOptedIn)} to receiving email.

Your clan ${display.message($fudgeability, $isShaolin)} to eat fudge with.

As you can see, I set up a bunch of ways of printing 0 vs. 1, and you can choose the right one based on the context.

This is fab for reusable strings you intend to output inline. For another example from this recent post, if you want to output a CSS class on a link if a Marketo Boolean (at first a String ""|"1" in Velocity, then converted to a Number 0|1) is true, you could do:

#set( $trackingClass = "{0,choice,0#mktNoTrack|1#}" )
<a class="${display.message($trackingClass, $canBeTracked)}" 
   href="http://www.example.com/page.html">Click here</a>

Converting Boolean-like Strings to Boolean-like Numbers

Ah yes, almost forgot this part.

Velocity and Java really don't want to see "" and "1" as Boolean-ish. $convert.toBoolean is completely useless in this case (the Strings aren't enough like Booleans to be converted).[2]

But you can make the Strings look numeric, then convert them to Numbers proper. Do this by prepending a "0" ("" becomes "0" so it's recognized as 0. "1" becomes "01", which is safe as it's still recognized as 1).

Like so:

#set( $canBeTracked = $convert.toNumber("0${lead.canBeTracked}") )

Now you have a real Number 0 or 1 to feed into $display.message as above.

I know it's hard to care!

I realize a lot of my patter about Strings and Booleans and Numbers and blah-blah-blah is overwhelming to a lot of you.

I'd guess this blog's readership breaks down to about 50% please-just-give-me-the-code-and-don't-explain it, 40% hopeful programmers who don't know where to start (but are pretty sure it isn't with deep stuff like this!), and perhaps 10% working programmers who care about this stuff. Datatypes are boring as heck, but really are the meat of programming: you could spend a whole second career (which is kind of what I'm doing myself!) actually understanding what you were doing during your first technical career.

Think of posts like this as a having a “long tail.” I'd like to think the junior people out there will come back to these posts later, when they have a few years under their belts, and understand enough to learn.


Notes

[1] Because of one of Java's truly strange behaviors, #if($field == 0) or #if($field == 1) will work as well — but only because 0 and 1 are between -127 and 127!

[2] convert.toBoolean will convert the String "false" to Boolean false and the String "true" to Boolean true. So there's that, I guess, if you happen to get that kind of input (reminds me of the worst parts of PHP).