Expand my Community achievements bar.

Enhance your AEM Assets & Boost Your Development: [AEM Gems | June 19, 2024] Improving the Developer Experience with New APIs and Events
SOLVED

AEM 6.5 | RTE List Content Issue

Avatar

Level 4

In my content fragment RTE field, it seems each list item has an extra new line.  

 

MukeshAEM_0-1687940910076.png

 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.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

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.

View solution in original post

3 Replies

Avatar

Level 4

@MukeshAEM You can read the RTE value in Java and then manipulate it using a Java API.

 

Regards,

Ayush

Avatar

Correct answer by
Community Advisor

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.

Avatar

Community Advisor

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.