问题
First let me say that using Struts2 + Freemarker is a real blast. Yet there's something is driving me crazy, because I cannot understand why it happens. I ask here as maybe someone else has an idea to share about it.
I've got an action, with a property. Say
private String myText;
Then I've got a setter and a getter:
public void setMyText(String myText)
{
this.myText = myText;
}
public String getMyText()
{
if (myText == null)
myText = "(empty)";
return this.myText;
}
The result (in struts.xml) is a freemarker result. So in my Freemarker template there's a line like the following:
<p>The text is: ${myText}</p>
Now consider I'm calling the action without any text parameter: say the url is
http:localhost:8080/myapp/myaction
As the getter provides a default value, when the action is processed and the result passed to my template, the property is set to the default; so I get (html on the browser side)
<p>The text is: (empty)</p>
If I call my action with the parameter set, instead (I mean with something like:
http:localhost:8080/myapp/myaction?myText=hallo
) things go wrong. Freemarker fires the following exception:
Exception occurred during processing request: For "${...}" content:
Expected a string or something automatically convertible to string
(number, date or boolean), but this has evaluated to a
sequence+extended_hash (String[] wrapped into f.e.b.ArrayModel)
It seems that "myText" is found twice... What am I doing wrong? Or, at least, is there anyone that can explain to me why it happens?
P.S.: it's really found twice; the following is a way to workaround the problem:
<#if myText?is_sequence>${myText[0]}<#else>${myText}</#if>
Yet it seems to me not viable to wrap every variable in that way.
P.P.S.: a further hint: in the freemarker template there's a call to another action some lines before. Something like:
<@s.action var="innerAction" name="getTable" namespace="/foo" />
If I comment the line above, everything works fine.
回答1:
The myText
could be a variable from the freemarker context, but if you want to use action property
<p>The text is: ${action.myText}</p>
Note that action
prefix is not required to access action properties. A property resolution method is applied when resolving freemarker variables:
Property Resoloution
Your action properties are automatically resolved - just like in a velocity view.
for example
${name}
will result instack.findValue("name")
, which generally results inaction.getName()
being executed.A search process is used to resolve the variable, searching the following scopes in order, until a value is found :
- freemarker variables
- value stack
- request attributes
- session attributes
- servlet context attributes
And later you can read what objects are accessible from the context.
Objects in the Context
The following variables exist in the FreeMarker views
- req - the current HttpServletRequest
- res - the current HttpServletResponse
- stack - the current OgnlValueStack
- ognl - the OgnlTool instance This class contains useful methods to execute OGNL expressions against arbitary objects, and a method to generate a select list using the pattern. (i.e. taking the name of the list property, a listKey and listValue)
- struts - an instance of StrutsBeanWrapper
action - the current Struts action
- exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view
The error might be caused by searches from the value stack and returning something that you didn't expect depending on the structure of the stack at the moment of execution. Adding a prefix to the variable to point out the exact location of the property should fix the redundancy in the code when searching in the value stack.
来源:https://stackoverflow.com/questions/32868080/freemarker-and-struts-2-sometimes-it-evaluates-as-a-sequenceextended-hash