Expand my Community achievements bar.

Need to add multifield child Resource as JSON in the Sling Component Exporter

Avatar

Level 5

Hi All,

 

I have a JSON for the component which is exported as below - 

"componentA": {
":type": "a/b/c",
"test": "Test String.",
"test1": "cf63d10c-5ad7-44d5-9986-e1673b21675c",
"test2": "#E5E5E5",
"test3": "false"
}

 

However, the component has an inherent logic - The sightly for the component calls a generic model which collects multi-field items as resource and displays its details.

<sly data-sly-use.testList="${'test.abc.core.models.multifieldcollection.MultifieldCollectionModel' @ resourcePath = resource.path , multifieldName='testList'}"
if (resourcePath != null && multifieldName != null) {
String multifieldResourcePath = String.join("/", resourcePath, multifieldName);
ResourceResolver resolver = resource.getResourceResolver();
Resource multifieldResource = resolver.getResource(multifieldResourcePath);
if (multifieldResource != null) {
multifieldResource.listChildren().forEachRemaining(multiCollection::add);
}
}

 

As you can see this helps fetches all the multi-field items but doesn't extend the same to JSON as there is no way to export this as @JsonProperty.

What should be the best way to export these items as part of the component's JSON?

 

Thanks,

NR

 

Topics

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

2 Replies

Avatar

Community Advisor

Hi @NageshRaja,

You’ll need to update the model MultifieldCollectionModel so that it implements Sling Model Exporter (e.g., ComponentExporter or ContainerExporter) and explicitly expose the list of child items using a getter annotated with @JsonProperty.

1. Update the Model
@Model(
    adaptables = Resource.class,
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL,
    resourceType = "your/component/resourceType",
    adapters = {MultifieldCollectionModel.class, ComponentExporter.class}
)
public class MultifieldCollectionModel implements ComponentExporter {

    @Self
    private Resource resource;

    private List<MultifieldItem> items;

    @PostConstruct
    protected void init() {
        items = new ArrayList<>();
        Resource childRes = resource.getChild("testList");
        if (childRes != null) {
            childRes.getChildren().forEach(child -> {
                items.add(child.adaptTo(MultifieldItem.class));
            });
        }
    }

    @JsonProperty("testList")
    public List<MultifieldItem> getItems() {
        return items;
    }

    @Override
    public String getExportedType() {
        return resource.getResourceType();
    }
}
2. Create the MultifieldItem Model
@Model(adaptables = Resource.class
public class MultifieldItem {
    
    @ValueMapValue
    private String itemTitle;

    @ValueMapValue
    private String itemDesc;

    public String getItemTitle() {
        return itemTitle;
    }

    public String getItemDesc() {
        return itemDesc;
    }
}
3. HTL Usage (optional)
<sly data-sly-use.model="com.example.MultifieldCollectionModel"
     data-sly-list.item="${model.testList}">
  <div>
    <h3>${item.itemTitle}</h3>
    <p>${item.itemDesc}</p>
  </div>
</sly>
You should then expect the resulting JSON output
{
  ":type": "your/component/resourceType",
  "testList": [
    {
      "itemTitle": "Title 1",
      "itemDesc": "Description 1"
    },
    {
      "itemTitle": "Title 2",
      "itemDesc": "Description 2"
    }
  ]
} 

 


Santosh Sai

AEM BlogsLinkedIn


Avatar

Community Advisor

Hi @NageshRaja ,

Try below solution:

1. Multifield Item Model

package com.project.core.models;

import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

import javax.inject.Inject;

@Model(adaptables = org.apache.sling.api.resource.Resource.class,
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MultifieldItem {

    @ValueMapValue
    private String itemTitle;

    @ValueMapValue
    private String itemDesc;

    public String getItemTitle() {
        return itemTitle;
    }

    public String getItemDesc() {
        return itemDesc;
    }
}

2. Component Model Exporter Class

package com.project.core.models;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.DefaultInjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.apache.sling.models.annotations.Default;
import org.apache.sling.models.annotations.injectorspecific.ChildResource;
import org.apache.sling.models.factory.ModelFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;

import com.adobe.cq.export.json.ComponentExporter;

@Model(adaptables = Resource.class,
       adapters = { ComponentExporter.class },
       resourceType = "project/components/component-a",
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
@Exporter(name = "jackson", extensions = "model.json")
public class ComponentAModel implements ComponentExporter {

    @Self
    private Resource resource;

    @Inject
    @Default(values = "")
    private String test;

    @Inject
    @Default(values = "")
    private String test1;

    @Inject
    @Default(values = "")
    private String test2;

    @Inject
    @Default(values = "")
    private String test3;

    @ChildResource(name = "testList")
    private List<Resource> testListResources;

    @Inject
    private ModelFactory modelFactory;

    private List<MultifieldItem> testList;

    @PostConstruct
    protected void init() {
        testList = new ArrayList<>();
        if (testListResources != null) {
            for (Resource res : testListResources) {
                MultifieldItem item = modelFactory.createModel(res, MultifieldItem.class);
                if (item != null) {
                    testList.add(item);
                }
            }
        }
    }

    @JsonProperty("test")
    public String getTest() {
        return test;
    }

    @JsonProperty("test1")
    public String getTest1() {
        return test1;
    }

    @JsonProperty("test2")
    public String getTest2() {
        return test2;
    }

    @JsonProperty("test3")
    public String getTest3() {
        return test3;
    }

    @JsonProperty("testList")
    public List<MultifieldItem> getTestList() {
        return testList;
    }

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

 3. HTL

<sly data-sly-use.model="com.project.core.models.ComponentAModel"
     data-sly-list.item="${model.testList}">
  <div class="multi-item">
    <h3>${item.itemTitle}</h3>
    <p>${item.itemDesc}</p>
  </div>
</sly>

Deployment Notes

Multifield Path Structure in CRXDE:

  - Author dialog must store multifield items under testList/ node.

  - Each item should have itemTitle, itemDesc as properties.

  - Example structure in /content:

component-a
  └── testList
      ├── item0
      │   ├── itemTitle = "Title 1"
      │   └── itemDesc  = "Description 1"
      └── item1
          ├── itemTitle = "Title 2"
          └── itemDesc  = "Description 2"