Your achievements

Level 1

0% to

Level 2

Tip /
Sign in

Sign in to Community

to gain points, level up, and earn exciting badges like the new
Bedrock Mission!

Learn more

View all

Sign in to view all badges

SOLVED

Need Jackson Exporter JSON response within SlingModel Class

aman_goyal_15
Level 2
Level 2

Hi Team,

Usecase:  Need to utilize model JSON response in the component sightly file. 

 

Basically, we use Jackson Exporter to get JSON response for SlingModel but it's a servlet call on the component resource. But my requirement is to get the JSON response for SlingModel from one of the getter functions of the Sling Model. 

something like - 

 

 

 

 

@Model(adaptables = {
        SlingHttpServletRequest.class
}, adapters = {
        Button2.class,
        ComponentExporter.class
}, resourceType = Button2Impl.RESOURCE_TYPE)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class Button2Impl implements Button2 {

@ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
private String label;

 public String getJsonString() {
  String json = "logic to get model json response like sling model exporter;
  return json;
 }
}
<sly data-sly-use.buttonProvider="${'com.sample.general.models.Button2'}">
<span> ${buttonProvider.jsonString} </span>
</sly>

 

 

 

 

Could anyone suggest how to achieve the above scenario? 

 

Thanks,

Aman

 

SlingModel Implementation Class -

 

 

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.sample.sample.data_providers.general.models.Button2;
import com.sample.sample.services.utils.LinkUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.OSGiService;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.factory.ExportException;
import org.apache.sling.models.factory.MissingExporterException;
import org.apache.sling.models.factory.ModelFactory;

import java.util.HashMap;
import java.util.Map;

@Model(adaptables = {
        SlingHttpServletRequest.class
}, adapters = {
        Button2.class,
        ComponentExporter.class
}, resourceType = Button2Impl.RESOURCE_TYPE)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class Button2Impl
        implements Button2
{
    public static final String RESOURCE_TYPE = "sample/components/general/button-2-0";

    
    private ResourceResolverFactory resourceResolverFactory;

    @SlingObject
    private ResourceResolver resourceResolver;
   
    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String iconAlignment;
    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String buttonAlignment;
    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String marginBottom;

    @SlingObject
    private Resource resource;
    @SlingObject
    private SlingHttpServletRequest request;
    @OSGiService
    ModelFactory modelFactory;
    
    
    public String getIconAlignment() { return iconAlignment; }

    
    public String getButtonAlignment() { return buttonAlignment; }

    
    @JsonIgnore
    public String getMarginBottom() { return marginBottom; }

    
    public String getExportedType() {
        return resource.getResourceType();
    }

    public String getJsonString() {
        Button2 buttonModel = request.adaptTo(Button2Impl.class); // tried adapting with resource as well but no luck 
        Map<String,String> options = new HashMap();
        String s = null;
        try {
            s = modelFactory.exportModel(buttonModel,ExporterConstants.SLING_MODEL_EXPORTER_NAME,String.class,options);
        } catch (ExportException e) {
            e.printStackTrace();
        } catch (MissingExporterException e) {
            e.printStackTrace();
        }
        return s;
    }

}

 

 

The solution to this query - 

 @JsonIgnore
    public String getJsonString() {
        ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        JsonNode node = mapper.valueToTree(this);
        String jsonString =null;
        try {
            jsonString = mapper.writeValueAsString(node);
        } catch (JsonProcessingException e) {
            e.getMessage();
        }
        return jsonString;
    }
1 Accepted Solution
shelly-goel
Correct answer by
Community Advisor
Community Advisor

Hi@aman_goyal_15 

Another option is to use 'ObjectMapper' (com.fasterxml.jackson.databind.ObjectMapper) to map the model object as json. Add below method in your sling model class and either map the Button resource object or custom map object (with the specific value you need in json).

public String getJsonString() {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.valueToTree(<button object>);
String jsonString ="no value";
try {
jsonString = mapper.writeValueAsString(node);
} catch (JsonProcessingException e) {
LOG.error("Cannot output json", e);
}
return jsonString;
}

You need to have the 'Button' object in the model

@Self @Via(type = ResourceSuperType.class)
private Button2 button;

 

View solution in original post

15 Replies
kunal23
Level 10
Level 10

You can use below code reference. Its a JSP code, you can convert it to Java and use in your sling model. The code uses Sling ModelFactory API to export a sling model. 

 

<%

//Get reference to model factory
org.apache.sling.models.factory.ModelFactory mf = sling.getService(org.apache.sling.models.factory.ModelFactory.class);

//Get sling model
Title title = slingRequest.adaptTo(com.adobe.cq.wcm.core.components.models.Title.class);


Map<String,String> options = new HashMap();
//Export and print json
String s = mf.exportModel(title,"jackson",String.class,options);
out.println(s);

%>

 

 

 

Output-

{"id":"title-d61980eb4b","linkDisabled":false,"type":"h3","text":"Standard",":type":"core/wcm/components/title/v2/title"}

 

aman_goyal_15
Level 2
Level 2
Thanks for your response. I tried the above approach but looks like it is not working. It is giving the null response while exporting model - String s = modelFactory.exportModel(buttonModel,ExporterConstants.SLING_MODEL_EXPORTER_NAME,String.class,options);
kunal23
Level 10
Level 10

Can you share more lines of code ?  I hope the buttonModel object is not null. The above code works for me. I tried it for Title core component and it works. 

aman_goyal_15
Level 2
Level 2

Attached the complete Java class in the query placeholder. Please check.

kunal23
Level 10
Level 10
Just try this piece of code - s = modelFactory.exportModel(this,"jackson",String.class,options);
aman_goyal_15
Level 2
Level 2
response is same with 'this' .. observing "Method threw 'java.lang.StackOverflowError' exception." when model factory tries to export.
BrianKasingli
Community Advisor
Community Advisor

@aman_goyal_15 

Another way is to do something like this within the servlet.

1. Set the response content-type.

2. Using business logic, locate the Sling resource you need.

3. Adapt the Sling resource to Button2Impl.

4. output the JSON string.

Example:

 

 @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
        response.setContentType(APPLICATION_JSON_UTF8);
        Resource buttonResource = request.getResourceResolver().getResource("/content/my-page/jcr:content/par/button");
        BUtton2 button2 = buttonResource.adaptTo(Button2Impl.class);
        if (button2 != null) {
            response.getWriter().write(button2.getJsonString());    
        }
        ...
    }

 

aman_goyal_15
Level 2
Level 2
Thanks for your response. Servlet is fine to expose the json once we get it. But I want to check for the approach to get the jsonString for Button2 model.
shelly-goel
Correct answer by
Community Advisor
Community Advisor

Hi@aman_goyal_15 

Another option is to use 'ObjectMapper' (com.fasterxml.jackson.databind.ObjectMapper) to map the model object as json. Add below method in your sling model class and either map the Button resource object or custom map object (with the specific value you need in json).

public String getJsonString() {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.valueToTree(<button object>);
String jsonString ="no value";
try {
jsonString = mapper.writeValueAsString(node);
} catch (JsonProcessingException e) {
LOG.error("Cannot output json", e);
}
return jsonString;
}

You need to have the 'Button' object in the model

@Self @Via(type = ResourceSuperType.class)
private Button2 button;

 

View solution in original post

aman_goyal_15
Level 2
Level 2
Thanks for your response. Sling Model is not working when we are trying to get the Button2 object with @Self and @Via. I was checking on the @Via(type = ResourceSuperType.class), it is basically when we want to override the core component sling model. https://github.com/adobe/aem-core-wcm-components/wiki/Delegation-Pattern-for-Sling-Models#step-2-cre...
shelly-goel
Community Advisor
Community Advisor

@aman_goyal_15Yes that's the case when you're extending core component model. For your custom component model, you can directly pass the resource and it will work (unless resource is null).

The constraint in this approach would be that it would not return the values post model transformation but the original values on the resource and trying to access the exported model object from within the model will lead to infinite recursion.

As I mentioned earlier you can pass the custom object as per your needs (json structure/ fields as needed) in the ObjectMapper and below logic would work. It gives the flexibility in terms of what fields you want to include/ exclude in the json and pipelines can be attached to the mapper.

 

@JsonIgnore
public String getJsonString() {
ObjectMapper mapper = new ObjectMapper().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
JsonNode node = mapper.valueToTree(new JsonConfig(getTitle(), getPretitle()));
String jsonString ="no value";
try {
jsonString = mapper.writeValueAsString(node);
} catch (JsonProcessingException e) {
LOG.error("Cannot output json", e);
}
return jsonString;
}

 

Here I created the custom object/ pojo as an inner class

private class JsonConfig {
private String title;
private String pretitle;
public JsonConfig() {};
public JsonConfig(String title, String pretitle) {
this.title = title;
this.pretitle = pretitle;
}
public String getTitle() {
return title;
}
public String getPretitle() {
return pretitle;
}
}

Please add @JsonIgnore annotations to all the methods that doesn't need to be part of .model.json request.

aman_goyal_15
Level 2
Level 2
Thank You Shelly .. It is working as expected. Passing 'this' reference as an argument to mapper.valueToTree is giving the actual Jackson exported model.json response.
Anudeep_Garnepudi
Community Advisor
Community Advisor

@aman_goyal_15 

Can you try 

@ScriptVariable
Resource resource;

and then

resource.adaptTo(Button2Impl.class);

 

kautuk_sahni
Community Manager
Community Manager
Thank you for sharing the solution with community.