Expand my Community achievements bar.

Guidelines for the Responsible Use of Generative AI in the Experience Cloud Community.
SOLVED

Node order not preserved using Sling post servlet

Avatar

Level 1

I am using sling post servlet to create a page reading a JSON. The json is valid and structured, but the resulting page that is created by the servlet has the nodes underneath in jumbled order. Is there a way to preserve the order as is in json? Please suggest. 

Topics

Topics help categorize Community content and increase your ability to discover relevant content.

1 Accepted Solution

Avatar

Correct answer by
Community Advisor

HI @ShriSa ,

If I understand the ask correctly. You have a JSON which has all the components data and using that JSON you want to create a page.
If yes, the order of nodes in a JSON object is not guaranteed to be preserved. If maintaining the order is crucial, you should utilize an array to explicitly represent the order.

{
  "jcr:primaryType": "cq:Page",
  "jcr:content": {
    "jcr:primaryType": "cq:PageContent",
    "components": [
      { "name": "component1", "sling:resourceType": "your/component/resource/type1", ... },
      { "name": "component2", "sling:resourceType": "your/component/resource/type2", ... },
      { "name": "component3", "sling:resourceType": "your/component/resource/type3", ... }
    ]
  }
}

Now, the "components" property is an array, and the order of components in the array will be preserved when you iterate over it.

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONObject;

public class PageCreationService {

    public void createPageWithComponents(ResourceResolver resolver, String pagePath, JSONObject json) {
        try {
            // Get or create the page resource
            Resource pageResource = ResourceUtil.getOrCreateResource(resolver, pagePath, "cq:Page", "cq:Page", true);

            // Get or create the page content node
            Resource contentResource = ResourceUtil.getOrCreateResource(resolver, pagePath + "/jcr:content", "cq:PageContent", "cq:PageContent", true);

            // Get the "components" array from the JSON
            JSONArray componentsArray = json.getJSONObject("jcr:content").getJSONArray("components");

            // Iterate over components array and create components
            for (int i = 0; i < componentsArray.length(); i++) {
                JSONObject componentJson = componentsArray.getJSONObject(i);
                String componentName = componentJson.getString("name");

                // Create the component under the page's content node
                ResourceUtil.getOrCreateResource(resolver, contentResource.getPath() + "/" + componentName, componentJson);
            }

            // Commit the changes
            resolver.commit();
        } catch (Exception e) {
            // Handle exceptions appropriately
            resolver.refresh();
            // Log or handle the exception as needed
        } finally {
            if (resolver.isLive()) {
                resolver.close();
            }
        }
    }
}


Thanks

View solution in original post

5 Replies

Avatar

Community Advisor

Preserving the order of JSON nodes in AEM using the Sling Post Servlet can be achieved by submitting the POST request using multipart/form-data encoding. This preserves the order of parameter application according to the original HTML form.

Here’s how you can do it:

  1. Ensure you are submitting the POST request using multipart/form-data encoding. This preserves the order of parameter application according to the original HTML form. To this avail, ensure to always include the enctype="multipart/form-data" attribute with the <form> tag.
    https://sling.apache.org/documentation/bundles/manipulating-content-the-slingpostservlet-servlets-po...

  2. Use .harray selector in GET requests: If you’re dealing with GET requests, you can use the .harray selector which causes child nodes to be output as arrays instead of objects, to preserve their order.
    https://sling.apache.org/documentation/bundles/rendering-content-default-get-servlets.html

 

Avatar

Level 3

Thanks for the suggestion @pulkitvashisth . In my case, I don't have a form. It is more of a post api call to my servlet which takes JSON as the input, verifies the structure and invokes sling request processor's process request to import the json to create nodes. In this process, I am not sure how to indicate that order needs to be preerved.

Avatar

Correct answer by
Community Advisor

HI @ShriSa ,

If I understand the ask correctly. You have a JSON which has all the components data and using that JSON you want to create a page.
If yes, the order of nodes in a JSON object is not guaranteed to be preserved. If maintaining the order is crucial, you should utilize an array to explicitly represent the order.

{
  "jcr:primaryType": "cq:Page",
  "jcr:content": {
    "jcr:primaryType": "cq:PageContent",
    "components": [
      { "name": "component1", "sling:resourceType": "your/component/resource/type1", ... },
      { "name": "component2", "sling:resourceType": "your/component/resource/type2", ... },
      { "name": "component3", "sling:resourceType": "your/component/resource/type3", ... }
    ]
  }
}

Now, the "components" property is an array, and the order of components in the array will be preserved when you iterate over it.

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONObject;

public class PageCreationService {

    public void createPageWithComponents(ResourceResolver resolver, String pagePath, JSONObject json) {
        try {
            // Get or create the page resource
            Resource pageResource = ResourceUtil.getOrCreateResource(resolver, pagePath, "cq:Page", "cq:Page", true);

            // Get or create the page content node
            Resource contentResource = ResourceUtil.getOrCreateResource(resolver, pagePath + "/jcr:content", "cq:PageContent", "cq:PageContent", true);

            // Get the "components" array from the JSON
            JSONArray componentsArray = json.getJSONObject("jcr:content").getJSONArray("components");

            // Iterate over components array and create components
            for (int i = 0; i < componentsArray.length(); i++) {
                JSONObject componentJson = componentsArray.getJSONObject(i);
                String componentName = componentJson.getString("name");

                // Create the component under the page's content node
                ResourceUtil.getOrCreateResource(resolver, contentResource.getPath() + "/" + componentName, componentJson);
            }

            // Commit the changes
            resolver.commit();
        } catch (Exception e) {
            // Handle exceptions appropriately
            resolver.refresh();
            // Log or handle the exception as needed
        } finally {
            if (resolver.isLive()) {
                resolver.close();
            }
        }
    }
}


Thanks

Avatar

Level 3

Many Thanks for the suggestion @Madhur-Madan . This sounds to be a promising approach. I shall check and update.

Avatar

Community Advisor
JSONObject componentsChildJSon = new JSONObject();
Field field = componentsChildJSon.getClass().getDeclaredField("map");
field.setAccessible(true);
field.set(componentsChildJSon, new LinkedHashMap<>());
field.setAccessible(false);
while (childsOfChild.hasNext()) {
Resource child = childsOfChild.next();
JSONObject itemsJson = new JSONObject();
for (Map.Entry<String, Object> entrySet : child.getValueMap().entrySet()) {
itemsJson.put(entrySet.getKey(), entrySet.getValue().toString());
}
componentsChildJSon.put(child.getName(), itemsJson);
}
componentsChildJSon.remove("jcr:primaryType");
componentsJson.put(childResource.getName(), componentsChildJSon);

You will have to ask json to use LinkedHashMap to solve your issue.
Hope the above code snippet will help to understand.
TUmesh THakur