1

I am using Struts 2 class which implements ModelDriven. I am able to pass the data from jQuery and save the data on the database.

When I try to retrieve the data back and pass it back to jQuery, I am not sure why it's not available in jQuery. I am sure I am missing something with the basic flow.

Here is my action class:

public HttpHeaders index() {
    model = projectService.getProjectDetails(project.getUserID());
    return new DefaultHttpHeaders("success").setLocationId("");
} 

@Override
public Object getModel() {
    return project;
}

public Project getProject() {
    return project;
}

public void setProject(Project project) {
    this.project = project;
}

Here is my jQuery:

function getProjectDetails() {
    var userID = localStorage.getItem('userID');
    var request = $.ajax({
        url : '/SUH/project.json',
        data : {
            userID : userID
        },
        dataType : 'json',
        type : 'GET',
        async : true
    });

    request.done(function(data) {
        console.log(JSON.stringify(data));
        $.each(data, function(index, element) {
            console.log('element project--->' + index + ":" + element);
            
        });
    });

    request.fail(function(jqXHR, textStatus) {
        console.log('faik');
    });
}

The model object in the Action class has all the data available but I tried to return model or project objects but both didn't work.

Roman C
  • 49,761
  • 33
  • 66
  • 176
CrazyMac
  • 462
  • 4
  • 19
  • I tried sending the model object back to the JQuery and I get a JSON error.. Should I format it to JSON and send it net.sf.json.util.CycleDetectionStrategy$StrictCycleDetectionStrategy.handleRepeatedReferenceAsObject(CycleDetectionStrategy.java:73) net.sf.json.JSONObject._fromBean(JSONObject.java:658) – CrazyMac Apr 26 '16 at 17:56
  • I'm not too familiar with this, but how does your ajax call know what function to call in the Action class? – xCRKx TyPHooN Apr 26 '16 at 20:12
  • Its based on the request type. As this is a GET request, it calls the index() method – CrazyMac Apr 27 '16 at 03:05
  • `getModel() { return project;}` but `model = ...` – Aleksandr M Apr 27 '16 at 09:19
  • I tried with both return model or project both giving me the same error. Now I am getting a JSON exception referring to the net.sf.json.JSONException: There is a cycle in the hierarchy!. I tried with @JsonBackReference by applying it to the child on parent class but didnt work.. Any help – CrazyMac Apr 28 '16 at 04:16
  • @CrazyMac Why are you using `async:true`, it's deprecated. – Roman C Apr 28 '16 at 11:16
  • @RomanC I will remove it. I was not aware of it. I added JsonIgnore and jsonbackreference to the child elements and I was able to save the data but when I try to retrieve, I get the same error. I am not sure as I am dealing with the same elements. Any guidance here? – CrazyMac Apr 28 '16 at 13:08
  • You don't save any data and these JsonIgnore and jsonbackreference are useless. – Roman C Apr 28 '16 at 13:46
  • ok I think I didnt provide enough info. I was able to add those annotations and fix it while saving the data but I see the same error while fetching the data as well and not able to figure it out – CrazyMac Apr 28 '16 at 16:18
  • If there is *There is a cycle in the hierarchy* then fix it. – Aleksandr M Apr 28 '16 at 20:22
  • I am not following you, I have a bean which is a parent to couple of others.. I am not sure how it works while pushing/saving the records but not working while fetching it. – CrazyMac Apr 29 '16 at 04:30
  • Get your model and try to serialize it to json by yourself, if it fails with *There is a cycle in the hierarchy*, then it is cycle in the hierarchy and you need to get rid of it. – Aleksandr M Apr 29 '16 at 09:37
  • This is my method in the action.. Do you want me to serialize this model object? How do I check the hierarchy issue ? – CrazyMac Apr 29 '16 at 10:29
  • Do you get an exception on serializing model? BTW don't forget to @CrazyMac mention people. – Aleksandr M Apr 29 '16 at 19:48
  • @AleksandrM Sure.. This is the exception I get net.sf.json.JSONException: There is a cycle in the hierarchy! – CrazyMac May 01 '16 at 02:54
  • @AleksandrM This is my struts method public HttpHeaders index() { //model = projectService.getProjectDetails(userID); model = objProjectDAOImpl.getProjectDetails(userID); return new DefaultHttpHeaders("success").setLocationId(""); } The object returned to model looks fine with a list of domain object. But when after the return statement I get the error below on hierarchy net.sf.json.JSONException: There is a cycle in the hierarchy! – CrazyMac May 01 '16 at 07:10
  • And your question is? *Looks fine* doesn't mean it is serializable. – Aleksandr M May 02 '16 at 09:06

1 Answers1

1

By default Struts2 REST plugin is using json-lib for serialization your beans. If you are using ModelDriven then it accesses your model directly when it processes a result. Since you are using the extension .json in the request URL the content type handler is selected by extension. It should be JsonLibHandler.

This handler is using JSONArray.fromObject(obj) if obj is an array or list or JSONObject.fromObject(obj) otherwise to get JSONObejct that could be serialized and written to the response.

The obj is the value returned by getModel(), in your case it will be project.

Because JsonLibHandler is using default JsonConfig you can't exclude properties from the bean to be serialized unless they are public fields.

The following features of json-lib could be powered with JsonConfig:

  • Cycle detection, there are two default strategies (default throws an exception), you can register your own
  • Skip transient fields when serailizing to JSON (default=don't skip) Skip JAP @Transient annotated methods when serailizing to JSON (default=don't skip)
  • Exclude bean properties and/or map keys when serailizing to JSON (default=['class','metaClass','declaringClass'])
  • Filters provide a finer detail for excluding/including properties when serializing to JSON or transforming back to Java

You can find this code snippets that allows you to exclude some properties.

Exclude properties

String str = "{'string':'JSON', 'integer': 1, 'double': 2.0, 'boolean': true}";  
JsonConfig jsonConfig = new JsonConfig();  
jsonConfig.setExcludes( new String[]{ "double", "boolean" } );  
JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON( str, jsonConfig );  
assertEquals( "JSON", jsonObject.getString("string") );        
assertEquals( 1, jsonObject.getInt("integer") );        
assertFalse( jsonObject.has("double") );     
assertFalse( jsonObject.has("boolean") );     

Exclude properties (with filters)

String str = "{'string':'JSON', 'integer': 1, 'double': 2.0, 'boolean': true}";  
JsonConfig jsonConfig = new JsonConfig();  
jsonConfig.setJsonPropertyFilter( new PropertyFilter(){    
   public boolean apply( Object source, String name, Object value ) {    
      if( "double".equals(value) || "boolean".equals(value) ){    
         return true;    
      }    
      return false;    
   }    
});    
JSONObject jsonObject = (JSONObject) JSONSerializer.toJSON( str, jsonConfig );  
assertEquals( "JSON", jsonObject.getString("string") );        
assertEquals( 1, jsonObject.getInt("integer") );        
assertFalse( jsonObject.has("double") );     
assertFalse( jsonObject.has("boolean") );

But you have an option to use your own ContentTypeHandler to override defaults.

The alternative is to use Jackson library to handle request. As described in the docs page: Use Jackson framework as JSON ContentTypeHandler.

The default JSON Content Handler is build on top of the JSON-lib. If you prefer to use the Jackson framework for JSON serialisation, you can configure the JacksonLibHandler as Content Handler for your json requests.

First you need to add the jackson dependency to your web application by downloading the jar file and put it under WEB-INF/lib or by adding following xml snippet to your dependencies section in the pom.xml when you are using maven as build system.

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-jaxrs</artifactId>
    <version>1.9.13</version>
</dependency>

Now you can overwrite the Content Handler with the Jackson Content Handler in the struts.xml:

<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="jackson" class="org.apache.struts2.rest.handler.JacksonLibHandler"/>
<constant name="struts.rest.handlerOverride.json" value="jackson"/>

<!-- Set to false if the json content can be returned for any kind of http method -->
<constant name="struts.rest.content.restrictToGET" value="false"/> 

<!-- Set encoding to UTF-8, default is ISO-8859-1 -->
<constant name="struts.i18n.encoding" value="UTF-8"/>

After that you can use @JsonIgnore annotation.

Roman C
  • 49,761
  • 33
  • 66
  • 176