[groovy-user] Binding preserved between successive calls to Template::make(map) with GStringTemplateEngine

55 views
Skip to first unread message

Atom

unread,
Dec 17, 2013, 1:54:35 PM12/17/13
to us...@groovy.codehaus.org
Hi,

I am using the GStringTemplateEngine and encountered this problem today:

// slurp will change 'books' to a List/Map structure
String text = """<% books = slurp(books) %>"""

GStringTemplateEngine engine = new GStringTemplateEngine()
Template template = engine.createTemplate(text)

// a String of JSON data
Map map = [books,other,data]

result = template.make(map) // OK FIRST TIME

// all changed except 'books'
map = [books,changed other data]

result = template.make(map) // Throws Exception because 'books' is now a
List

So, on repeat calls, the value of books is preserved

bug? my error? how to work around without getting a new Template?





--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807.html
Sent from the groovy - user mailing list archive at Nabble.com.

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


Atom

unread,
Dec 17, 2013, 2:12:49 PM12/17/13
to us...@groovy.codehaus.org
Just to be clear, 'books' starts out as a json string and finishes as a
List.

However, even though Teamplate::make(map) is called with the same data,
'books' in the template has been preserved.

The map is actually a Google ImmutableMap.of(somemap) and so the map
provided really is the original values.

... the values in the template closure are being preserved and then causing
the problem.

Just to clarify.



--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807p5717808.html

Guillaume Laforge

unread,
Dec 17, 2013, 2:41:31 PM12/17/13
to Groovy User
Could you please show a full reproducible example?
--
Guillaume Laforge
Groovy Project Manager
Head of Groovy Development at SpringSource
http://www.springsource.com/g2one

Atom

unread,
Dec 17, 2013, 3:17:15 PM12/17/13
to us...@groovy.codehaus.org
I hope this shows. Pasted from a Java app:

-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running net.csbe.live.orders.TestGstrings
Class of books: class java.lang.String
result:
Class of books: class java.util.ArrayList


@Test
public void testGStringTemplate() throws Exception {

String text = "<%"
+ " println(\"Class of books:
${books.getClass()}\") \n"
+ "
\n"
+ " def slurper = JsonSlurper.newInstance()
\n"
+ " books = slurper.parseText(books)
\n"
+ "
\n"
+ "
\n"
+ " def passed = [\"creditCard\":true,
\n"
+ " \"paid\":false,
\n"
+ " \"earlyPaymentCredit\":false,
\n"
+ " \"books\":books
\n"
+ " ]
\n"
+ "%>";

GStringTemplateEngine engine = new GStringTemplateEngine();
Template template = engine.createTemplate(text);

String bookjson = "[{\"info\":{\"csbeid\":\"B24470\",\"title\":\"1.
CUE FOR
TREASON\",\"isbn\":\"0-7730-3014-X\",\"year\":\"1997\"},\"GOOD\":{\"qty\":\"1\",\"quality\":\"GOOD\",\"unitPrice\":\"13.27\",\"total\":\"13.27\"},\"USABLE\":{\"qty\":\"1\",\"quality\":\"USABLE\",\"unitPrice\":\"6.06\",\"total\":\"6.06\"},\"gross\":{\"qty\":\"2.00\",\"total\":\"19.33\"}}]";

Map data = ImmutableMap.of("books", bookjson, "JsonSlurper",
JsonSlurper.class);

StringWriter sw = new StringWriter();
// round one
template.make(data).writeTo(sw);
System.out.println("result: " + sw.toString());

// round two
template.make(data).writeTo(sw);
System.out.println("result: " + sw.toString());

}


StackTrace:

groovy.lang.MissingMethodException: No signature of method:
groovy.json.JsonSlurper.parseText() is applicable for argument types:
(java.util.ArrayList) values: [[[USABLE:[unitPrice:6.06, total:6.06, qty:1,
quality:USABLE], ...]]]
Possible solutions: parseText(java.lang.String), parse(java.io.File),
parse(java.io.Reader), parse(java.net.URL)
at
org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55)
at
org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46)
at
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at groovy.json.JsonSlurper$parseText.call(Unknown Source)
at
groovy.tmp.templates.GStringTemplateScript1$_getTemplate_closure1.doCall(GStringTemplateScript1.groovy:5)
... [more]



--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807p5717811.html

Atom

unread,
Dec 21, 2013, 7:29:05 PM12/21/13
to us...@groovy.codehaus.org

Guillaume Laforge

unread,
Dec 24, 2013, 4:52:39 AM12/24/13
to Groovy User
At least, in your previous email, the problem sounds different than what you initially explained.
The error you're getting is that you're passing a list as argument of the parseText() method, which actually expects a String, not a list.

Guillaume

Atom

unread,
Dec 24, 2013, 7:06:56 AM12/24/13
to us...@groovy.codehaus.org
I am not sure I am explaining or maybe I do not see something you see. This
should not fail:

// slurp will change 'books' to a List/Map structure
String text = """<% books = slurp(books) %>"""

GStringTemplateEngine engine = new GStringTemplateEngine()

// template for re-use.
Template template = engine.createTemplate(text)

// a String of JSON data
Map map1 = ["books",books]
result = template.make(map1) // OK FIRST TIME

// Throws Exception because 'books' is now a List
// but it is not a List in the binding
Map map2 = ["books",books]
result = template.make(map2)



--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807p5717878.html

Guillaume Laforge

unread,
Dec 24, 2013, 7:23:22 AM12/24/13
to Groovy User
At the end of this snippet, you do:

Map map2 = ["books",books]
result = template.make(map2)

Notice that ["books", books] is indeed a List (not a map, no ":" to separate the keys).
And trying to assign that list inside Map map2, you should be getting a GroovyCastException as you can put a list into a map variable that way.

So this is likely not the exact code that you're using.
But my impression is that you indeed pass a list instead of a map.
A list is a comma separated list of values surrounded by square brackets, whereas a map is a comma separated list of key/value pairs joined by colons, and the whole surrounded by square brackets.

Atom

unread,
Dec 24, 2013, 7:48:52 AM12/24/13
to us...@groovy.codehaus.org
Ooops I posted a bad example. Here goes again:

String text = "<% books = books.split(\",\") %>";

StringWriter sw = new StringWriter();
GStringTemplateEngine engine = new GStringTemplateEngine();

Template template = engine.createTemplate(text);

Map data = new HashMap(){{ put("books", "a,b,c,d");}};


// round one sucess
template.make(data).writeTo(sw);
System.out.println("result: " + sw);


// round two fails
data = new HashMap(){{ put("books", "a,b,c,d");}};
template.make(data).writeTo(sw);
System.out.println("result: " + sw);



--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807p5717880.html

Cédric Champeau

unread,
Dec 24, 2013, 7:55:47 AM12/24/13
to us...@groovy.codehaus.org
There is indeed a problem with GStringTemplateEngine. I suggest you file
a JIRA issue with this example. If possible, you can work with
SimpleTemplateEngine which doesn't have this issue.

Thanks!

Le 24/12/2013 13:48, Atom a �crit :
> String text = "<% books = books.split(\",\") %>";
>
> StringWriter sw = new StringWriter();
> GStringTemplateEngine engine = new GStringTemplateEngine();
>
> Template template = engine.createTemplate(text);
>
> Map data = new HashMap(){{ put("books", "a,b,c,d");}};
>
>
> // round one sucess
> template.make(data).writeTo(sw);
> System.out.println("result: " + sw);
>
>
> // round two fails
> data = new HashMap(){{ put("books", "a,b,c,d");}};
> template.make(data).writeTo(sw);
> System.out.println("result: " + sw);


--
C�dric Champeau
SpringSource - Pivotal
http://spring.io/
http://www.gopivotal.com/
http://twitter.com/CedricChampeau

Atom

unread,
Dec 24, 2013, 8:13:06 AM12/24/13
to us...@groovy.codehaus.org
What is the difference, document says that GStrings can also be used?

Then what is the purpose of the GStringTemplate?

"The template engine uses JSP style <% %> script and <%= %> expression
syntax or GString style expressions. The variable 'out' is bound to the
writer that the template is being written to."





--
View this message in context: http://groovy.329449.n5.nabble.com/Binding-preserved-between-successive-calls-to-Template-make-map-with-GStringTemplateEngine-tp5717807p5717882.html
Sent from the groovy - user mailing list archive at Nabble.com.

Cédric Champeau

unread,
Jan 16, 2014, 4:38:28 AM1/16/14
to us...@groovy.codehaus.org
I have created the issue (you're lucky I remembered about it ;)) and
fixed it: https://jira.codehaus.org/browse/GROOVY-6521

Le 24/12/2013 14:13, Atom a �crit :
Reply all
Reply to author
Forward
0 new messages