Uploaded image for project: 'Struts 2'
  1. Struts 2
  2. WW-4176

Struts2 JSON Plugin: Send Map with Strings as Key to JSON Action is ignored, Numeric Keys will work and mapped

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Not A Problem
    • Affects Version/s: 2.3.14.3
    • Fix Version/s: 2.3.34, 2.5.13
    • Component/s: Plugin - JSON
    • Labels:
      None
    • Environment:

      Tomcat6, Struts 2.3.14.3,JSON Plugin 2.3.14.3,jquery

      Description

      Sending Map from javascript/jsp to JSON Action, when the Keys are numeric everything works, when the keys are Strings then these entries are thrown away while the mapping process in the JSON Action takes place.
      Important note: If i create a map in the JSON Action and send it to the client everything is ok.

      alert which shows the Server generated JSON:

      ServerJSON{"1":"1","2":"ANOTHERVALUE","ANOTHERKEY":"1","KEY":"VALUE"}
      

      If i look into the sended data from javascript side in Eclipse i see in debug mode:

      this=>myJSONMap:{2=[Ljava.lang.String;@3caf7a1f, 23=[Ljava.lang.String;@247aa859}
      

      Alert shows on client side:

      ClientJSON{"2":["ANOTHERVALUE"],"23":["1"]}
      

      Hope it's clear where the problem is. Bug or misunderstood anything?
      Would like to have a List of keys,values where the keys can be String/Long/Int and the values can be String/Long/int.

      The direction Server generated Map=>JSON is ok, sending the same from client side will lose the entries with a String key.

      Further i tried also to specify my Map like

      private Map<String, String> propertyValueMap = new LinkedHashMap<String, String>(); 
      

      and set the getters/setters but don't work either.

      Here is a part of my code:
      JSP PAGE:

      <form action="" id="testForm1">
      testForm1<input type="submit"/>
      </form>
      <script>
      $("#testForm1").submit(function(event){
      event.preventDefault(); //Cancel the default action (navigation) of the click.
      var data={"1":"1","2":"ANOTHERVALUE","ANOTHERKEY":"1","KEY":"VALUE"};
      var sentData={};
      sentData["myJSONMap"]=data;
      $.getJSON("<s:url namespace="/ajax" action="testMap"/>", sentData ,function(data3) {
      alert("Server generated JSON"+JSON.stringify(data3.mymap));
      alert("Client generated JSON"+JSON.stringify(data3.myJSONMap));
      });
      });
      

      JSON Action config:

      <struts>
      <package name="PIM_JSONDataPackage" namespace="/ajax" extends="struts-default,json-default">
      <action name="testMap" method="testMap" class="eu.mtd.actions.JSON_DropDownPropertyValuesAction">
      <result type="json" />
      <param name="noCache">true</param>
      </action>
      </package>
      </struts>
      

      JSON Action

      public class JSON_DropDownPropertyValuesAction extends ActionSupport{
      	private static final long serialVersionUID = 1L;
      	private Map session;
          @SuppressWarnings("rawtypes")
      	private Map mymap = new HashMap();
          @SuppressWarnings("rawtypes")
      	private Map myJSONMap = new HashMap();
      public JSON_DropDownPropertyValuesAction(){}
      	@SuppressWarnings("unchecked")
      	public String testMap(){
      		mymap.put("KEY", "VALUE");
      		mymap.put("1", "1");
      		mymap.put("ANOTHERKEY", "1");
      		mymap.put("2", "ANOTHERVALUE");
      		//Creates JSON: {"1":"1","2":"ANOTHERVALUE","ANOTHERKEY":"1","KEY":"VALUE"}
      		return SUCCESS;
      	}
      public String execute() {
              return SUCCESS;
      	}
      
      	@SuppressWarnings("rawtypes")
      	public Map getMymap() {
      		return mymap;
      	}
      
      	@SuppressWarnings("rawtypes")
      	public void setMymap(Map mymap) {
      		this.mymap = mymap;
      	}
      	
      	@SuppressWarnings("rawtypes")
      	public Map getMyJSONMap() {
      		return myJSONMap;
      	}
      	
      	@SuppressWarnings("rawtypes")
      	public void setMyJSONMap(Map myJSONMap) {
      		this.myJSONMap = myJSONMap;
      	}
      	public Map getSession() {
      		return session;
      	}
      
      	public void setSession(Map session) {
      		this.session = session;
      	}
      

        Issue Links

          Activity

          Hide
          yasser.zamani Yasser Zamani added a comment -

          OK, according to my work, it seems that this issue is not related to Struts2 JSON Plugin. Instead, ParametersInterceptor skips *not accepted parameters* by checking regular expression:

          ParametersInterceptor.java:145
              public static final String ACCEPTED_PARAM_NAMES = "\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['\\w+'\\])|(\\('\\w+'\\)))*";
          

          i.e. a word expression inside brackets is only valid when that word is inside single-quotes. So, I tried following instead which worked well

          index.jsp
          <%@taglib prefix="s" uri="/struts-tags"%>
          <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
          <form action="" id="testForm1">
          	testForm1<input type="submit" />
          </form>
          <script type="text/javascript">
          	$("#testForm1").submit(
          			function(event) {
          				event.preventDefault(); //Cancel the default action (navigation) of the click.
          				var data = {
          					"1" : "1",
          					"2" : "ANOTHERVALUE",
          					"'ANOTHERKEY'" : "1", /* using single-quotes for string keys */
          					"'KEY'" : "VALUE" /* using single-quotes for string keys */
          				};
          				var sentData = {};
          				sentData["myJSONMap"] = data;
          				$.getJSON("<s:url namespace="/ww4176" action="testMap"/>",
          						sentData, function(data3) {
          							alert("Server generated JSON"
          									+ JSON.stringify(data3.mymap));
          							alert("Client generated JSON"
          									+ JSON.stringify(data3.myJSONMap));
          						});
          			});
          
          	
          </script>
          
          Show
          yasser.zamani Yasser Zamani added a comment - OK, according to my work, it seems that this issue is not related to Struts2 JSON Plugin . Instead, ParametersInterceptor skips * not accepted parameters * by checking regular expression: ParametersInterceptor.java:145 public static final String ACCEPTED_PARAM_NAMES = "\\w+((\\.\\w+)|(\\[\\d+\\])|(\\(\\d+\\))|(\\['\\w+'\\])|(\\('\\w+'\\)))*" ; i.e. a word expression inside brackets is only valid when that word is inside single-quotes. So, I tried following instead which worked well index.jsp <%@taglib prefix= "s" uri= "/struts-tags" %> <script src= "http: //code.jquery.com/jquery-1.10.1.min.js" ></script> <form action= "" id=" testForm1"> testForm1<input type= "submit" /> </form> <script type= "text/javascript" > $( "#testForm1" ).submit( function(event) { event.preventDefault(); //Cancel the default action (navigation) of the click. var data = { "1" : "1" , "2" : "ANOTHERVALUE" , "'ANOTHERKEY'" : "1" , /* using single-quotes for string keys */ "'KEY'" : "VALUE" /* using single-quotes for string keys */ }; var sentData = {}; sentData[ "myJSONMap" ] = data; $.getJSON( "<s:url namespace=" /ww4176 " action=" testMap "/>" , sentData, function(data3) { alert( "Server generated JSON" + JSON.stringify(data3.mymap)); alert( "Client generated JSON" + JSON.stringify(data3.myJSONMap)); }); }); </script>
          Hide
          yasser.zamani Yasser Zamani added a comment -

          Maybe we can have an improvement for ParametersInterceptor!

          I attached a proposal patch as my suggestion. After applying that, ParametersInterceptor tries to fix parameter names by covering string indices with single-quotes e.g [2a_z] -> ['2a_z'].

          I tested it with latest code and works as expected.

          Show
          yasser.zamani Yasser Zamani added a comment - Maybe we can have an improvement for ParametersInterceptor ! I attached a proposal patch as my suggestion. After applying that, ParametersInterceptor tries to fix parameter names by covering string indices with single-quotes e.g [2a_z] -> ['2a_z'] . I tested it with latest code and works as expected.
          Hide
          victorsosa victorsosa added a comment - - edited

          This issue could be fixed by this issue WW-4560

          Show
          victorsosa victorsosa added a comment - - edited This issue could be fixed by this issue WW-4560
          Hide
          yasser.zamani Yasser Zamani added a comment - - edited

          Lukasz Lenart my four years ago understanding was very wrong

          By my today several hours work, I think you can close this issue as not a problem!

          Stefan Abendroth's codes not work because it is not a duty for $.getJSON to set Content-Type to application/json, so, Struts JSON Plugin's interceptor just ignores it for any further processing (reference)!

          My following modified configuration works for me:

          JSP PAGE:

          <%@taglib prefix="s" uri="/struts-tags"%>
          <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
          <head>
              <title>WW-4176</title>
          </head>
          <body>
          <form action="" id="testForm1">
              <input type="hidden"  name="${_csrf.parameterName}"   value="${_csrf.token}"/>
              testForm1<input type="submit"/>
          </form>
          <script>
              $("#testForm1").submit(function(event){
                  event.preventDefault(); //Cancel the default action (navigation) of the click.
                  var token = $("input[name='_csrf']").val();
                  var data={"1":"1","2":"ANOTHERVALUE","ANOTHERKEY":"1","KEY":"VALUE"};
                  var sentData={"myJSONMap":data};
                  $.ajax({
                      type: "POST",
                      url: "<s:url namespace="/" action="testMap"/>",
                      contentType: "application/json; charset=utf-8",
                      dataType: "json",
                      data: JSON.stringify(sentData),
                      headers: {
                          'X-CSRF-TOKEN':token
                      },
                      success: function(json) {
                          alert("Client generated JSON"+JSON.stringify(json));
                      },
                      error: function (xhr, textStatus, errorThrown) {
                          alert(xhr.responseText);
                      }
                  });
              });
          </script>
          </body>
          </html>
          

          JSON Action config:

          <package name="default" namespace="/" extends="struts-default,json-default">
          		<action name="testMap" method="testMap" class="me.zamani.yasser.ww_convention.actions.JSON_DropDownPropertyValuesAction">
          			<interceptor-ref name="json"></interceptor-ref>
                      <result name="success" type="json">
          			<param name="noCache">true</param>
                      </result>
          		</action>
          </package>
          
          Show
          yasser.zamani Yasser Zamani added a comment - - edited Lukasz Lenart my four years ago understanding was very wrong By my today several hours work, I think you can close this issue as not a problem! Stefan Abendroth 's codes not work because it is not a duty for $.getJSON to set Content-Type to application/json , so, Struts JSON Plugin's interceptor just ignores it for any further processing ( reference )! My following modified configuration works for me: JSP PAGE: <%@taglib prefix= "s" uri= "/struts-tags" %> <script src= "http://code.jquery.com/jquery-1.10.1.min.js" > </script> <%@ page contentType= "text/html;charset=UTF-8" language= "java" %> <html> <head> <title> WW-4176 </title> </head> <body> <form action= "" id=" testForm1"> <input type= "hidden" name= "${_csrf.parameterName}" value= "${_csrf.token}" /> testForm1 <input type= "submit" /> </form> <script> $( "#testForm1" ).submit(function(event){ event.preventDefault(); //Cancel the default action (navigation) of the click. var token = $( "input[name='_csrf']" ).val(); var data={ "1" : "1" , "2" : "ANOTHERVALUE" , "ANOTHERKEY" : "1" , "KEY" : "VALUE" }; var sentData={ "myJSONMap" :data}; $.ajax({ type: "POST" , url: " <s:url namespace=" / " action=" testMap "/> " , contentType: "application/json; charset=utf-8" , dataType: "json" , data: JSON.stringify(sentData), headers: { 'X-CSRF-TOKEN':token }, success: function(json) { alert( "Client generated JSON" +JSON.stringify(json)); }, error: function (xhr, textStatus, errorThrown) { alert(xhr.responseText); } }); }); </script> </body> </html> JSON Action config: <package name= "default" namespace= "/" extends= "struts-default,json-default" > <action name= "testMap" method= "testMap" class= "me.zamani.yasser.ww_convention.actions.JSON_DropDownPropertyValuesAction" > <interceptor-ref name= "json" > </interceptor-ref> <result name= "success" type= "json" > <param name= "noCache" > true </param> </result> </action> </package>

            People

            • Assignee:
              lukaszlenart Lukasz Lenart
              Reporter:
              sabendroth Stefan Abendroth
            • Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development