Issue returning JSON value

About Struts2-JSON Plugin

Struts2 JSON Plugin works in a particular way:

The JSON plugin provides a "json" result type that serializes actions into JSON.

It serializes the entire Action into JSON, except

  • transient properties
  • properties without the Getter

If you don't want the whole Action to be serialized, but only one object of your choice, you can specify a Root Object:

Use the "root" attribute(OGNL expression) to specify the root object to be serialized.

it can be done in struts.xml like this:

<result type="json">
    <param name="root">
        objectToBeSerialized
    </param>
</result>

while the Action should have:

private CustomObject objectToBeSerialized;

public CustomObject getObjectToBeSerialized(){
    return this.objectToBeSerialized;
}

Where CustomObject can be a Primitive, a String, an Array, etc...

Using it this way (the way it is built for), you can return SUCCESS and ERROR like in any other AJAX Struts2 Action, without breaking the framework conventions, and access the serialized JSON object from the callback function of the AJAX jQuery call like any other field (if using rootObject, the "data" of var handledata = function(data) would be your object, otherwise it would be your Action).


About your specific case

In your case, assuming your object structure looks like this

row1 [col1, col2], 
row2 [col1, col2], 
rowN [col1, col2]

you could create a List of an object with two columns:

Value Object

public class MyRow implements Serializable {
    private static final long serialVersionUID = 1L;

    private String col1; 
    private String col2;

    // Getters
    public String getCol1(){ 
        return this.col1; 
    }
    public String getCol2(){ 
        return this.col2; 
    }
}

Action class

public class PartAction implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private List<MyRow> rows;   

    // Getter
    public  List<MyRow> getRows() { 
        return this.rows; 
    } 

    public String finder() {
        String result = Action.SUCCESS;
        rows = new ArrayList<MyRow>();

        try {
            Iterator it = findList.iterator();
            while(it.hasNext()) {
                SearchResult part = (SearchResult) it.next();
                MyRow row = new MyRow();
                row.setCol1(part.getcol1());
                row.setCol2(part.getcol2());
                rows.add(row);
            }
        } catch (Exception e) {
            result = Action.ERROR;
            log.error(e);
        }
        return result;
    }  
} 

Struts.xml

<package name="default" namespace="/ajax" extends="json-default">
    <action name="finder" class="action.Part" method="finder" name="finder">
        <result type="json" >
            <param name="root">
                rows
            </param>
        </result>
  </action>
</package>

To test it in the AJAX Callback Function, simply use $.each :

var handledata = function(data) {
    $.each(data, function(index) {
        alert(data[index].col1);
        alert(data[index].col2);
    });     
}

Of course you can use a List<List<String>> instead of a Custom object, or any other object structure you like more than this: it was only to get you the idea.


A dataType : 'json' is used by jQuery Ajax to specify a data type that is expected to return by the success callback function when the action and result is executed, and a response returned from the server.

dataType (default: Intelligent Guess (xml, json, script, or html))

Type: String

The type of data that you're expecting back from the server. If none is specified, jQuery will try to infer it based on the MIME type of the response (an XML MIME type will yield XML, in 1.4 JSON will yield a JavaScript object, in 1.4 script will execute the script, and anything else will be returned as a string).

The URL should correctly point to the action mapping. Assume it will be in the default namespace, otherwise you should modify URL and mapping to add the namespace attribute.

<script type="text/javascript">
  $(function() {
    $("#dialog-form").dialog ({
      autoOpen: true,
      height: 500,
      width: 750,
      modal: true,
      buttons : {
        "Search" : function() {
          $.ajax({
            url : '<s:url action="part" />',
            success : function(data) {
              //var obj = $.parseJSON(data);
              var obj = data;
              alert(JSON.stringify(obj));
            }
          });
        }
      }
    });
  });
</script>

Returning json result type is not needed if you build the JSONObject manually. You can return text as stream result then convert a string to JSON if needed.

struts.xml:

<package name="default" extends="struts-default">
  <action name="part" class="action.PartAction" method="finder">    
    <result type="stream">
      <param name="contentType">text/html</param>
      <param name="inputName">stream</param>
    </result>
  </action>
</package>

Action:

public class PartAction extends ActionSupport {

  public class SearchResult {
    private String col1;
    private String col2;

    public String getCol1() {
      return col1;
    }

    public void setCol1(String col1) {
      this.col1 = col1;
    }

    public String getCol2() {
      return col2;
    }

    public void setCol2(String col2) {
      this.col2 = col2;
    }

    public SearchResult(String col1, String col2) {
      this.col1 = col1;
      this.col2 = col2;
    }
  }

  private InputStream stream;

  //getter here
  public InputStream getStream() {
    return stream;
  }

  private List<SearchResult> findList = new ArrayList<>();

  public List<SearchResult> getFindList() {
    return findList;
  }

  public void setFindList(List<SearchResult> findList) {
    this.findList = findList;
  }

  private String list() {
    JSONObject jo = new JSONObject();
    try {
      for (SearchResult part : findList) {
        jo.put("col1", part.getCol1());
        jo.put("col2", part.getCol2());
      }
      System.out.println("--------->:"+jo.toString());
    } catch (Exception e) {
      e.printStackTrace();
      System.out.println(e.getMessage());
    }
    return jo.toString();
  }

  @Action(value="part", results = {
    @Result(name="stream", type="stream", params = {"contentType", "text/html", "inputName", "stream"}),
    @Result(name="stream2", type="stream", params = {"contentType", "application/json", "inputName", "stream"}),
    @Result(name="json", type="json", params={"root", "findList"})
  })
  public String finder() {
    findList.add(new SearchResult("val1", "val2"));
    stream = new ByteArrayInputStream(list().getBytes());
    return "stream2";
  }
}

I have placed different results with result type and content type to better describe the idea. You could return any of these results and return JSON object either stringified or not. The stringified version requires to parse returned data to get the JSON object. You can also choose which result type better serializes to suit your needs but my goal was to show that if you need to serialize the simple object then json plugin is not necessary to get it working.

References:

  • How can we return a text string as the response
  • How to convert JSONObject to string