Friday, March 6, 2009

XPages: Regular Expressions in Server Side JS are poorly implemented

I was trying to do some simple templating using JSON in XPages today, and thought: "Hey! Finally I can use the power of JavaScript regular expressions!".

I was really disappointed when I found out how poorly implemented regular expressions seem to be. I first wrote/tested the code in the FireBug console.

First fail of XPages - passing a function as the second parameter of String.replace:
var object = { firstName: 'Tommy', lastName: 'Valand' };
var template = '{lastName}, {firstName}';
template.replace( /\{(\w+)\}/g, function( item, key ){
return object[key] || '';
The result should be: "Valand, Tommy"
The result is: ","

String.replace in Server Side JS does not accept a function as the second parameter.

Then I thought I could use String.match to pick out all the template-parts of the string:
var template = '{lastName}, {firstName}';
template.match( /\{(\w+)\}/g );
The result should be: ["{lastName}", "{firstName}"]
The result is: ["{lastName}", "lastName"]

String.match in Server Side JS ignores the global modifier.

The worst part.. The above JavaScript code works with Client JavaScript in Notes, which I believe was introduced in Notes 5.0.. :\

I know the JavaScript in XPages is built on Java, but that is not an excuse. The Regular Expression engine in Java is even more powerful than the engine in JavaScript.

I've posted a bug-report in the forums (and ranted on my blog). Not sure if there is more I can do to influence IBM to fix this.. :\

If you want to do something along the lines of what I was trying to do. Here is my workaround-code:

function mapJsonToTemplate( jsonString, template ){
var jsonObject = eval( '(' + jsonString + ')' );

// In REAL JavaScript, string.replace can have a function as a second argument
// This is not possible in XPages JavaScript, so I have to get every item
// and replace them item by item, in a loop

var itemRegExp = /\{(\w+)\}/;
var result = template;

var matchArr;
while( matchArr = result.match( itemRegExp ) ){
result = result.replace( matchArr[0], jsonObject[ matchArr[1] ] );

return result;

mapJsonToTemplate( '{"firstName":"Tommy","lastName":"Valand"}', '{lastName}, {firstName}' )
-> "Valand, Tommy"

The reason for taking a string as input will be explained in my next blogpost, which should be along shortly.


Theo Heselmans (Xceed) said...

this has bitten me too. I tried to use the global mod for the match function, only to discover it didn't work. Very frustrating.
Thanks for your workaround.

sean cull said...

bit me too, ened up using @replacesubstring

Anonymous said...

Still nog working as expected in XPages, after some cursing used your work around!

Olle said...

You saved my day!

Anonymous said...

JSS is supporting functions but there is a bug. (Probably in the JS to Java converting)
The problem could be solved by adding a extra set of parenthesis at the end of the Regular Expression code.
If you only got one parenthesis it leave the position for the first hit.

from your ex.

var object = { firstName: 'Tommy', lastName: 'Valand' };
var template = '{lastName}, {firstName}';
template.replace( /\{(\w+)\}()/g, function( item, key ){
return object[key] || '';

this return the correct answer.

Tommy Valand said...

Nice :)

How in the world did you figure this out?

Anonymous said...

I had a regular expression with several capturing groups. When adding a breakpoint inside the function I saw that the attributes to the functions had values.

But first I did try your "replaceAllFn" in

but I did not manage it to work with multiple capturing groups and if the string contains multiple instance that should be replaced.