Velocitip: double quotes (") aren’t interchangeable with single quotes (')

The Marketo Person’s First Name is “Sandy”. What will the output be of this Velocity snippet?

Test 1:
Hello ${lead.FirstName}, we're your ${esc.h}1 fan!

Test 2:
#set( $greeting = "Hello ${lead.FirstName}, we're your ${esc.h}1 fan!" )
${greeting}

Test 3:
#set( $greeting = "Hello ${lead.FirstName}, we${esc.s}re your ${esc.h}1 fan!" )
${greeting}

Test 4:
#set( $greeting = 'Hello ${lead.FirstName}, we${esc.s}re your ${esc.h}1 fan!' )
${greeting}

Test 5:
#set( $greeting = "Hello ${lead.FirstName}, we''re your ${esc.h}1 fan!" )
${greeting}

Test 6:
#set( $greeting = 'Hello ${lead.FirstName}, we''re your ${esc.h}1 fan!' )
${greeting}

Test 7:
#[[
#set( $greeting = 'Hello ${lead.FirstName}, we''re your ${esc.h}1 fan!' )
Hello ${lead.FirstName}, we${esc.s}re your ${esc.h}1 fan!
]]#

Test 8:
#set( $greeting = 'Hello ${lead.FirstName}, we''re your ${esc.h}1 fan!' )
#evaluate( $greeting )

Before you click below to get the answer, rest assured that few people on Earth are prepared to get all 8 outputs right, since so few people specialize in Velocity in the first place! But you should want to be in that group if you’re writing Velocity for emails: unlike other Velocity-powered contexts, ain’t no takebacks with Velocity {{my.tokens}}.[1]

Click to reveal answer

Test 1:
Hello Sandy, we're your #1 fan!

Test 2:
Hello Sandy, we're your #1 fan!

Test 3:
Hello Sandy, we're your #1 fan!

Test 4:
Hello ${lead.FirstName}, we${esc.s}re your ${esc.h}1 fan!

Test 5:
Hello Sandy, we''re your #1 fan!

Test 6:
Hello ${lead.FirstName}, we're your ${esc.h}1 fan!

Test 7:
#set( $greeting = 'Hello ${lead.FirstName}, we''re your ${esc.h}1 fan!' )
Hello ${lead.FirstName}, we${esc.s}re your ${esc.h}1 fan!

Test 8:
Hello Sandy, we're your #1 fan!

So how many of those lines were surprising? It’s okay, we’re all friends here: I’m betting at least Test 4, Test 7, or Test 8 wasn’t what you expected.

Tests 4 and 6, explained: different quotes

Though many Velocity examples unfortunately treat them as interchangeable, double-quoted strings are evaluated differently from single-quoted strings. Only double-quoted strings interpolate (a.k.a. expand/replace/deference) ${references} or other special Velocity syntax. Single-quoted strings treat their contents literally, so no replacements are made.

This doesn’t make Velocity weird! Just means it belongs to the “single and double quotes mean different things” family of languages. This family includes PHP, Java (not JavaScript), C/C++, C# and Go. Of these, VTL works most like PHP.

In contrast, the “single and double quotes are interchangeable” family includes JavaScript, Python and R.[2] In those languages, you can freely choose ' or ".[3]  The only rule — hopefully obvious — is once you choose your outside quote type, that same type needs to be escaped if it’s inside the string (usually backslash-escaped \' or \\", sometimes doubled '' or "").

Test 1, explained: VTL treats output lines (lines outside #directives) the same as double-quoted strings

You surely got Test 1 right. But after looking at Test 4, you might wonder why Test 1 is interpolated, so ${lead.FirstName} becomes Sandy and ${esc.h} becomes a literal #.

Couldn’t it just as easily go the other way? Well, VTL wouldn’t be much use as a template language if it didn’t do that, since you’d have to separately #set anything containing variables. Velocity is verbose enough as it is! So “pure output” lines are interpolated.

Test 7, explained:  #[[ ]]# syntax is a special signal to turn off both interpolation and parsing

Was afraid you’d be unimpressed unless I threw in a really new case. In Test 7, I use the special wrapper #[[ ]]# that basically means “inside these tags, treat everything literally.”

That means even the #set() is treated as plain text, and of course the next line is as well. (Not a contradiction of the rule about pure output because the line is inside a #[[ directive, so isn’t pure.)

Test 8, explained: #evaluate is a second pass through the parser

#evaluate is key to using JSON-formatted data in Marketo, as it allows you to parse string fields to create living, multidimensional Velocity/Java objects.

I’m not doing anything that fancy here, but the idea is the same: #evaluate($code_like_string) parses the string (exactly the same string as in Test 6) as if it were a line of Velocity code in pure output mode, i.e. one of the interpolated modes.

Therefore, the ${lead.FirstName} is replaced on that second pass.[4]

About ${esc.h}

If you don’t recognize ${esc.h} yet, you need to. That’s how you include a literal # when it would usually be treated as starting a #directive. For example, in this line:

Tag your kitty mukbang videos with #omakatse for a retweet.

#omakatse is assumed to be a directive. If it doesn’t happen to exist as a directive, it won’t be a problem (and will be printed literally) but if it does exist, it’ll throw a fatal error. Since a custom #macro could define a directive called #omakatse at any time, the way to make your code futureproof is:

Tag your kitty mukbang videos with ${esc.h}omakatse for a retweet.

And about two single quotes  ''

Overall, there are 4 ways to print a literal single quotation mark in Velocity:

(1) plain old single quote ', where it doesn’t conflict with any other syntax

(2) the EscapeTool variable ${esc.s} which contains the single character '

(3) Unicode escape \u0027

(4) two single quotes in a row '' (not to be confused with the double quote "!)

However, only one of these works within a single-quoted string and that’s (4).

(1) won’t work because single quotes inside a single-quoted string break the string and throw a fatal error; (2) won’t work because, as you learned today, single-quoted strings don’t interpolate references like ${esc.s}; and (3) won’t work because (though you wouldn’t have known this without testing) Unicode escapes are considered a form of interpolation.

Takeaways

My advice for manageable Velocity code:

  • use double quotes everywhere, so interpolation is on by default
  • to disable interpolation, use the EscapeTool shortcuts to make special characters un-special
Notes

[1] Unlike Velocity-powered websites or other backend services, which can be quickly fixed if there’s a problem, we can’t change content once it’s in somebody’s Inbox.

[2] JSON only supports double quotes. Remember, JSON is a tiny subset of JavaScript. If you paste valid JSON into JS, you can assign it to a JS variable to create strings, numbers or complex nested objects. But not, by any stretch, the reverse: most JavaScript objects cannot be converted to JSON.

[3] Note neither JS nor Python interpolate at all within basic ' or " strings. Only with JS backtick ` and Python f-strings would you even think about interpolation.

[4] Things can get weirder because of the number of times Marketo parses tokens, so test, test, test! And always use real emails as your final test — don’t sign off based on Send Sample or Preview. (For small formatting changes I may trust Preview-by-List, but you need a lot of experience first.)