Ignore Marketo datatypes — expect strings in Velocity

More in this 2018 post on Booleans, Numbers, and Strings. And in this 2017 must-read on DateTime conversions/calculations.

As a precursor to a longer series of Velocity posts, let's look at data types in the scripting environment vs. in the Marketo database.

The rule (singular) is simple: in my tests every lead field is exported as a string into Velocity, regardless of whether it's a Boolean, Date, DateTime, Integer, Score, or String type within Field Management and the individual Lead UI.

Example #1: Dates

The frustration expressed by users DL and LM in this Community thread is understandable. They expect a Marketo Date field to already be a Velocity Date (or a Java Date) object but instead it's a String in date-y format like "2016-08-17".

Luckily, you can make a true Date from the date-y String using the Velocity ConversionTool, which Marketo includes as the default $convert:

#set( $myRealDate = $convert.parseDate($lead.myDateyString,'yyyy-MM-dd') )

Yet this example is deliberately naive, since by using a date-y string instead of datetime-y string I am sidestepping the often huge timezone problems that occur in Marketo's Velocity implementation. You can see me peppering the Community with examples of this, but I don't have a dedicated blog post yet. and (as of late 2017) there's a dedicated post on DateTimes here.

Example #2: Booleans

Related confusion is illustrated in this Marketo article (which I've been using as a bit of a punching bag lately!) where there's a so-called “Boolean” comparison like this:

#if( $lead.myBooleyString == "1" )

Here, the custom lead field myBooleyString is a firmly a Boolean in Field Management, displayed as a checkbox in the Marketo UI and as a checkbox (by default) on Marketo Forms. But in Marketo's Velocity implementation a Boolean becomes a string with two possible values: the string "1" and the empty string ""!

Now, if that's not surprising to any level of developer, I don't know what is. I assume the Boolean gets exported that way by accident. The problem is that both "1" and empty "" are truthy in Velocity:

#set( $booleyT = "1" )
#set( $booleyF = "" )
#if( $booleyT ) 
  booleyT is true
#else
  booleyT is false
#end
#if( $booleyF ) 
  booleyF is true
#else
  booleyF is false
#end

This code will output:

  booleyT is true
  booleyF is true

While the string "0" is also truthy, I think it's way more confusing to export the empty string than it would be to export "0". Of course Marketo absolutely can't change this now or they would break thousands of correctly written scripts. At the same time I wonder how many people's current scripts are actually broken because of screwed-up comparisons.

Example #3: Scores and Integers

With Marketo's numeric field types, playing very dumb about strings actually turns out okay. This comparison happens to work as expected:

#if( $lead.myScoreyString >= 100 )

It works because when you have a String (object) on one side and an int (primitive value) on the other, they are compared numerically as you want. (Weirdly, this isn't explicitly mentioned in the Velocity documentation; it's the way most type-juggling works, like in JS or PHP, and the way Java itself works, but Velocity in theory should not require knowledge of/trust in Java's rules.)

In contrast, if you were to get “smart” and try something like this without thinking it through:

#if( $lead.myScoreyString >= "100" )

Now you're in a world of hurt. Even though Velocity will let you match $lead.myScoreyString == "100" (which wouldn't work in Java — again, with differences like these it's frustrating that the Velo docs are so sparse, as if anything missing just works like Java) now it's comparing them as strings. Say someone's Lead Score is 80: "80" >= "100" is true! (Because alphabetically "8" comes after "1"!) So either let the automatic numeric comparison do its job, or manually convert both sides to numbers.

This is a Marketo thing, not a Velocity thing

To be clear, Velocity has complete support for typed Java objects, so Marketo made the deliberate choice to cast everything as a string first. Even if fields kept their original types, Velocity can do cross-type comparisons (==) with their own set of rules, with the benefit that you would have to do far fewer conversions with $convert. Can't say why Marketo chooses to do what they do, just don't hold it against Velocity!

Overall thoughts

In all, everything's-a-string isn't so bad if you're aware of it. If you want to display a numeric field as currency, for example, it's just as well that it be a string to start with. If you want to see if a field is empty you can use $var.isEmpty() instead of having to switch between different “emptiness” checks based on type.

I'll post a follow-up on datatypes of Custom Object properties. Not sure if the rule holds there as well.

And if anyone has found any exceptions for lead fields, I'm all ears.

Edited 2016-08-18: Added Scores section

Added a breakdown of how things can go wrong with numeric field types.