In my content fragment RTE field, it seems each list item has an extra new line.
The JSON content returned for the above is:
<p>Checking list content behaviour.</p>\n<ul>\n<li>list1</li>\n<li>list2</li>\n<li>list3</li>\n</ul>\n
I don't see any option to remove these extra new lines in the RTE field. Wondering if this is a default behaviour and if there is a way to fix it. Thanks in advance.
Solved! Go to Solution.
Views
Replies
Total Likes
Hello @MukeshAEM -
The extra new lines in the RTE field's list items are a default behavior in AEM's content fragment JSON representation. It includes new line characters ("\n") for better readability and formatting purposes.
If you want to remove these extra new lines from the JSON content, you can customize the serialization process of the content fragment model. Here's an example of how you can achieve this:
1. Create a new Java class that extends the WCMUsePojo class:
package com.example.core.models;
import org.apache.sling.api.resource.Resource;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData;
import com.adobe.cq.wcm.core.components.models.datalayer.ComponentDataUtil;
import com.adobe.cq.wcm.core.components.models.datalayer.ExporterDataImpl;
import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilder;
import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilderFactory;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.components.EditConfig;
import com.day.cq.wcm.api.components.EditContext;
import com.day.cq.wcm.api.components.InplaceEditingConfig;
import com.day.cq.wcm.api.components.InplaceEditingConfig.Type;
import com.day.cq.wcm.api.components.RefreshMode;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.api.policies.ContentPolicy;
import com.day.cq.wcm.api.policies.ContentPolicyMapping;
import com.day.cq.wcm.api.policies.ContentPolicyManager;
import com.day.cq.wcm.commons.WCMUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CustomContentFragmentModel extends WCMUsePojo implements ComponentExporter {
private static final String COMPONENT_DATA_NAME = "componentData";
private static final String COMPONENT_RESOURCE_TYPE = "my-project/components/content/contentfragment";
private static final String SLING_MODEL_CLASS = "com.example.core.models.CustomContentFragmentModel";
private String jsonString;
private JsonNode componentData;
private ComponentData data;
private String resourceType;
@Override
public void activate() throws Exception {
HttpServletRequest request = getRequest();
Resource resource = getResource();
Style currentStyle = WCMUtils.getStyle(request);
// Get the content policy mapping for the resource
ContentPolicyManager policyManager = resource.getResourceResolver().adaptTo(ContentPolicyManager.class);
ContentPolicyMapping policyMapping = policyManager.getPolicyMapping(resource);
if (policyMapping != null) {
ContentPolicy contentPolicy = policyMapping.getPolicy();
currentStyle = contentPolicy.getStyle(resource);
}
// Get the component resource type
Component component = resource.adaptTo(Component.class);
if (component != null) {
resourceType = component.getResourceType();
}
// Build the data layer for the component
DataLayerBuilder dataLayerBuilder = DataLayerBuilderFactory.create(resource, currentStyle, request,
componentDataUtil, COMPONENT_RESOURCE_TYPE, SLING_MODEL_CLASS);
data = dataLayerBuilder.build();
// Serialize the component data to JSON
ObjectMapper objectMapper = new ObjectMapper();
StringWriter stringWriter = new StringWriter();
objectMapper.writeValue(stringWriter, data);
jsonString = stringWriter.toString();
// Parse the JSON string to retrieve the component data
componentData = objectMapper.readTree(jsonString);
}
public String getJsonString() {
return jsonString;
}
public JsonNode getComponentData() {
return componentData;
}
@Override
public String getExportedType() {
return COMPONENT_RESOURCE_TYPE;
}
}
2. Update your Sling Model class to use this new class as a property:
mpackage com.example.core.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.ExporterOption;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.Self;
import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.wcm.api.Page;
import javax.inject.Inject;
@Model(
adaptables = Resource.class,
adapters = { CustomContentFragmentModel.class },
resourceType = "my-project/components/content/contentfragment",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION,
options = {
@ExporterOption(name = ExporterConstants.OPTION_SERIALIZE_PRETTY, value = "true")
}
)
public class CustomContentFragmentModel {
@Self(injectionStrategy = InjectionStrategy.OPTIONAL)
private Resource resource;
@Inject
private Page currentPage;
@Inject
private String title;
// ... other properties and methods
}
3. Update your Sightly file to use the updated Sling Model:
<sly data-sly-use.customContentFragmentModel="com.example.core.models.CustomContentFragmentModel"
data-sly-use.customContentFragmentModelJson="com.fasterxml.jackson.databind.ObjectMapper">
<!-- Render the modified JSON without extra new lines -->
${customContentFragmentModelJson.writeValueAsString(customContentFragmentModel.componentData)}
</sly>
Now with this implementation, you can retrieve the JSON data for the content fragment model and then serialize it using the ObjectMapper without the extra new lines.
@MukeshAEM You can read the RTE value in Java and then manipulate it using a Java API.
Regards,
Ayush
Hello @MukeshAEM -
The extra new lines in the RTE field's list items are a default behavior in AEM's content fragment JSON representation. It includes new line characters ("\n") for better readability and formatting purposes.
If you want to remove these extra new lines from the JSON content, you can customize the serialization process of the content fragment model. Here's an example of how you can achieve this:
1. Create a new Java class that extends the WCMUsePojo class:
package com.example.core.models;
import org.apache.sling.api.resource.Resource;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.datalayer.ComponentData;
import com.adobe.cq.wcm.core.components.models.datalayer.ComponentDataUtil;
import com.adobe.cq.wcm.core.components.models.datalayer.ExporterDataImpl;
import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilder;
import com.adobe.cq.wcm.core.components.models.datalayer.builder.DataLayerBuilderFactory;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.components.EditConfig;
import com.day.cq.wcm.api.components.EditContext;
import com.day.cq.wcm.api.components.InplaceEditingConfig;
import com.day.cq.wcm.api.components.InplaceEditingConfig.Type;
import com.day.cq.wcm.api.components.RefreshMode;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.api.policies.ContentPolicy;
import com.day.cq.wcm.api.policies.ContentPolicyMapping;
import com.day.cq.wcm.api.policies.ContentPolicyManager;
import com.day.cq.wcm.commons.WCMUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CustomContentFragmentModel extends WCMUsePojo implements ComponentExporter {
private static final String COMPONENT_DATA_NAME = "componentData";
private static final String COMPONENT_RESOURCE_TYPE = "my-project/components/content/contentfragment";
private static final String SLING_MODEL_CLASS = "com.example.core.models.CustomContentFragmentModel";
private String jsonString;
private JsonNode componentData;
private ComponentData data;
private String resourceType;
@Override
public void activate() throws Exception {
HttpServletRequest request = getRequest();
Resource resource = getResource();
Style currentStyle = WCMUtils.getStyle(request);
// Get the content policy mapping for the resource
ContentPolicyManager policyManager = resource.getResourceResolver().adaptTo(ContentPolicyManager.class);
ContentPolicyMapping policyMapping = policyManager.getPolicyMapping(resource);
if (policyMapping != null) {
ContentPolicy contentPolicy = policyMapping.getPolicy();
currentStyle = contentPolicy.getStyle(resource);
}
// Get the component resource type
Component component = resource.adaptTo(Component.class);
if (component != null) {
resourceType = component.getResourceType();
}
// Build the data layer for the component
DataLayerBuilder dataLayerBuilder = DataLayerBuilderFactory.create(resource, currentStyle, request,
componentDataUtil, COMPONENT_RESOURCE_TYPE, SLING_MODEL_CLASS);
data = dataLayerBuilder.build();
// Serialize the component data to JSON
ObjectMapper objectMapper = new ObjectMapper();
StringWriter stringWriter = new StringWriter();
objectMapper.writeValue(stringWriter, data);
jsonString = stringWriter.toString();
// Parse the JSON string to retrieve the component data
componentData = objectMapper.readTree(jsonString);
}
public String getJsonString() {
return jsonString;
}
public JsonNode getComponentData() {
return componentData;
}
@Override
public String getExportedType() {
return COMPONENT_RESOURCE_TYPE;
}
}
2. Update your Sling Model class to use this new class as a property:
mpackage com.example.core.models;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.ExporterOption;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.Self;
import com.adobe.cq.export.json.ExporterConstants;
import com.day.cq.wcm.api.Page;
import javax.inject.Inject;
@Model(
adaptables = Resource.class,
adapters = { CustomContentFragmentModel.class },
resourceType = "my-project/components/content/contentfragment",
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION,
options = {
@ExporterOption(name = ExporterConstants.OPTION_SERIALIZE_PRETTY, value = "true")
}
)
public class CustomContentFragmentModel {
@Self(injectionStrategy = InjectionStrategy.OPTIONAL)
private Resource resource;
@Inject
private Page currentPage;
@Inject
private String title;
// ... other properties and methods
}
3. Update your Sightly file to use the updated Sling Model:
<sly data-sly-use.customContentFragmentModel="com.example.core.models.CustomContentFragmentModel"
data-sly-use.customContentFragmentModelJson="com.fasterxml.jackson.databind.ObjectMapper">
<!-- Render the modified JSON without extra new lines -->
${customContentFragmentModelJson.writeValueAsString(customContentFragmentModel.componentData)}
</sly>
Now with this implementation, you can retrieve the JSON data for the content fragment model and then serialize it using the ObjectMapper without the extra new lines.
Out of the box, AEM Core Components, text component. utilizes the Sling Model Exporter (Jackson) will not export individual pieces of content in the rich-text-area; as of the default, it will only export the "text" property.
If you wish to export custom properties for your text component, you would need to extend the WCM Core Component's Text component's sling model by using the delegation pattern. Next you'd want to create custom logic from the backend which will extract and select HTML elements that are set under the text property, and then expose a new object. This new object will then be exported by the Sling Model Exporter when there are getters for the variable set. Alternatively, instead of writing getter methods, you should be encouraged to use the Lombok Java Library, Using Project Lombok in AEM Sling Models | AEM 6.5 | Java 11 – AEM Queries & Solutions (wordpress.co....
Alternatively, you can use the WCM Core List Component, which it exports the list items as expected.
Views
Likes
Replies
Views
Like
Replies