Node order not preserved using Sling post servlet | Community
Skip to main content
January 22, 2024
Solved

Node order not preserved using Sling post servlet

  • January 22, 2024
  • 3 replies
  • 1311 views

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. 

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by Madhur-Madan

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

3 replies

pulkitvashisth
Community Advisor
Community Advisor
January 22, 2024

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-post.html

  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

 
January 23, 2024

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.

Madhur-Madan
Community Advisor
Madhur-MadanCommunity AdvisorAccepted solution
Community Advisor
January 23, 2024

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

January 24, 2024

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

Umesh_Thakur
Community Advisor
Community Advisor
February 1, 2024
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