javaspringstruts2strutsognl

Is OGNL expression support List<Map<String, String>>?


As for as I know, Struts2's type converter will work like this :

I declared a map Map<String,String> a4booking in my OrderContext's field, and in my JSP file the input tag's name is orderContext.a4booking.name and user entered 'Daniel', after submitting the , the a4booking map will have a key:value like { "name" : "Daniel" }, so the 'name' will become a key.

Now, when I declared a List<Map<String,String>> datas there is something wrong.

Action:

package com.clicugo.action;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.InterceptorRef;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.apache.struts2.convention.annotation.ResultPath;
import org.springframework.context.annotation.Scope;

@ParentPackage("clicugo-default")
@Namespace("/")
@Scope("prototype")
@InterceptorRef("clicugoStack")
@ResultPath("/WEB-INF/pages/test")
public class OGNLTestAction extends DefaultCliCugoAction {

    private List<Map<String, String>> datas = new ArrayList<Map<String,String>>();
    private List<HashMap<String, String>> datasHM = new ArrayList<HashMap<String,String>>();
    
    
  @Action(value = "ognl-test", results = { @Result(name = SUCCESS, type = "dispatcher", location = "ognlTest.jsp") })
  public String ognlTest() throws Exception {
    List<Map<String, String>> datas = getDatas();
    System.out.println();
    return SUCCESS;

  }

    public List<Map<String, String>> getDatas() {
        return datas;
    }

    public void setDatas(List<Map<String, String>> datas) {
        this.datas = datas;
    }

    public List<HashMap<String, String>> getDatasHM() {
        return datasHM;
    }

    public void setDatasHM(List<HashMap<String, String>> datasHM) {
        this.datasHM = datasHM;
    }
  
  
}

jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!DOCTYPE html>

<html>
<head>
    <title>Insert title here</title>
</head>
<body>
  <s:form action="ognl-test" method="POST">
        <s:textfield name="datas[0].testKey" ></s:textfield>
        <s:textfield name="datas[1].testKey" ></s:textfield>
        <s:textfield name="datas[2].testKey" ></s:textfield>
        <s:submit></s:submit>
    </s:form>
</body>
</html>

console error message:

2021-12-01 16:39:36,279 WARN  [http-nio-8080-exec-9] com.opensymphony.xwork2.util.logging.commons.CommonsLogger (CommonsLogger.java:68) - Error setting value [[Ljava.lang.String;@c9b3cb5] with expression [datas[2].testKey]
com.opensymphony.xwork2.XWorkException
    at com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor.getProperty(XWorkListPropertyAccessor.java:113) ~[xwork-core-2.3.32.jar:2.3.32]
    at ognl.OgnlRuntime.getProperty(OgnlRuntime.java:2417) ~[ognl-3.0.19.jar:?]
    at ognl.ASTProperty.getValueBody(ASTProperty.java:114) ~[ognl-3.0.19.jar:?]
    at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[ognl-3.0.19.jar:?]
    at ognl.SimpleNode.getValue(SimpleNode.java:258) ~[ognl-3.0.19.jar:?]
    at ognl.ASTChain.setValueBody(ASTChain.java:222) ~[ognl-3.0.19.jar:?]
    at ognl.SimpleNode.evaluateSetValueBody(SimpleNode.java:220) ~[ognl-3.0.19.jar:?]
    at ognl.SimpleNode.setValue(SimpleNode.java:301) ~[ognl-3.0.19.jar:?]
    at ognl.Ognl.setValue(Ognl.java:710) ~[ognl-3.0.19.jar:?]
    at com.opensymphony.xwork2.ognl.OgnlUtil$1.execute(OgnlUtil.java:296) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlUtil$1.execute(OgnlUtil.java:288) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:383) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlUtil.setValue(OgnlUtil.java:288) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlValueStack.trySetValue(OgnlValueStack.java:183) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlValueStack.setValue(OgnlValueStack.java:170) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.OgnlValueStack.setParameter(OgnlValueStack.java:152) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.setParameters(ParametersInterceptor.java:303) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:221) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73) [struts2-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91) [struts2-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253) [struts2-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:140) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) [struts2-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189) [xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.StayfunSSOInterceptor.intercept(StayfunSSOInterceptor.java:57) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.MemberLoginCheckInterceptor.intercept(MemberLoginCheckInterceptor.java:113) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.LoginCheckInterceptor.intercept(LoginCheckInterceptor.java:65) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.dontforgetme.interceptor.DontForgetMeInterceptor.intercept(DontForgetMeInterceptor.java:71) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.IChannelGidKeepInterceptor.intercept(IChannelGidKeepInterceptor.java:75) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.SalesPowerInterceptor.intercept(SalesPowerInterceptor.java:87) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.HostMappingInterceptor.intercept(HostMappingInterceptor.java:299) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.SslCheckInterceptor.intercept(SslCheckInterceptor.java:62) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at com.clicugo.struts2.interceptor.TimingInterceptor.intercept(TimingInterceptor.java:58) [classes/:?]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:245) [xwork-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54) [struts2-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:575) [struts2-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81) [struts2-core-2.3.32.jar:2.3.32]
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) [struts2-core-2.3.32.jar:2.3.32]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at com.clicugo.filter.ExtractHTMLFilter.doFilter(ExtractHTMLFilter.java:114) [classes/:?]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at com.clicugo.filter.ForwardFilter.doFilter(ForwardFilter.java:87) [classes/:?]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at com.clicugo.filter.VerifyParameters.doFilter(VerifyParameters.java:57) [classes/:?]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at com.clicugo.filter.EncodingFilter.doFilter(EncodingFilter.java:30) [classes/:?]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at org.apache.catalina.filters.HttpHeaderSecurityFilter.doFilter(HttpHeaderSecurityFilter.java:126) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at org.apache.catalina.filters.RequestFilter.process(RequestFilter.java:205) [catalina.jar:8.5.63]
    at org.apache.catalina.filters.RemoteAddrFilter.doFilter(RemoteAddrFilter.java:60) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:8.5.63]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:8.5.63]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [catalina.jar:8.5.63]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [catalina.jar:8.5.63]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:544) [catalina.jar:8.5.63]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [catalina.jar:8.5.63]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [catalina.jar:8.5.63]
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:698) [catalina.jar:8.5.63]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [catalina.jar:8.5.63]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:353) [catalina.jar:8.5.63]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:616) [tomcat-coyote.jar:8.5.63]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-coyote.jar:8.5.63]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:831) [tomcat-coyote.jar:8.5.63]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1629) [tomcat-coyote.jar:8.5.63]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:8.5.63]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) [?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) [?:?]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:8.5.63]
    at java.base/java.lang.Thread.run(Thread.java:832) [?:?]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'java.util.Map': Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)
    at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:250) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1003) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowire(AbstractAutowireCapableBeanFactory.java:339) ~[spring-beans-3.0.5.RELEASE.jar:3.0.5.RELEASE]
    at com.opensymphony.xwork2.spring.SpringObjectFactory.buildBean(SpringObjectFactory.java:194) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor.getProperty(XWorkListPropertyAccessor.java:111) ~[xwork-core-2.3.32.jar:2.3.32]
    ... 104 more

It means the Ognl.setValue() doesn't allow to put a map object into a List?

Also it says "hint: specify index/type/name arguments for simple parameters to avoid type ambiguities", so I use the HashMap instead, but I got another error message like :

2021-12-01 16:37:10,650 ERROR [http-nio-8080-exec-5] com.clicugo.action.DefaultExceptionHandler (DefaultExceptionHandler.java:50) - Exception occurred in defaultExceptionHandler, message is No result defined for action com.clicugo.action.OGNLTestAction and result input
com.opensymphony.xwork2.config.ConfigurationException: No result defined for action com.clicugo.action.OGNLTestAction and result input
    at com.opensymphony.xwork2.DefaultActionInvocation.executeResult(DefaultActionInvocation.java:374) ~[xwork-core-2.3.32.jar:2.3.32]
    at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:276) ~[xwork-core-2.3.32.jar:2.3.32]
    at ....

Is there something wrong in my sample code? And how do I put the Map into a List with OGNL expression?


Solution

  • I add a custom MapConverter for the xwork-conversion.properties, and it works!

    package com.struts2.converter;
    
    import java.util.Map;
    
    import org.apache.struts2.util.StrutsTypeConverter;
    
    public class MapConverter extends StrutsTypeConverter {
    
        @Override
      public Object convertFromString(Map context, String[] values, Class toClass) {
        
        String value = values[0];
        return value;
      }
    
        @Override
        public String convertToString(Map arg0, Object o) {
            return o.toString();
        }
    }