Hi all,
i am trying to populate a node (nt:unstructured) with properties and values taken from a json file.
i am getting issues in populating arraylist of strings to the jcr node. The below error is being reported
javax.jcr.ValueFormatException: Can not assign multiple values to single valued property:
The property set on last line of the below code creates single-valued property and not multi-valued as i want.
Resource resource = request.getResourceResolver().getResource(aemPath);
Node node = resource.adaptTo(Node.class);
Value[] values = new Value[ArrayList.size()];
node = resource.adaptTo(Node.class);
Value[] values = new Value[ArrayList.size()];
ValueFactory valueFactory = node.getSession().getValueFactory();
for(int index=0; index<ArrayList.size(); index++){
values[index] = valueFactory.createValue(ArrayList.get(index));
}
node.setProperty(aemFieldName, values);
@aanchal-sikka, @Jennifer_Dungan, @yuhuisg, @EstebanBustamante, @Harwinder-singh, @arunpatidar, @Rohan_Garg
Solved! Go to Solution.
Views
Replies
Total Likes
Your code looks correct but for some reason the property being set is classified as single type even though values array is being passed to it.
One minor point I can take out is as per Adding_And_Writing_Properties
setProperty(String name, Value[] values);
The property type of the property will be that specified by the node type of this node.
If the property type of the supplied Value objects is different from that required, then a best-effort conversion is attempted
If the conversion fails, a ValueFormatException is thrown.
I am not sure what will make a forced conversion in this case. Can you try the below snip once ?
Property property = node.setProperty(aemFieldName, values);
if (!property.isMultiple()) {
property.remove();
}
Hello @aem_noob
We shouldn't use Node API, until absolutely necessary.
Following blog shares snippets about getting and setting properties (single & multi-valued)
https://aem-ti2fap.blogspot.com/2021/08/use-cases-of-modifiablevaluemap-in-aem.html
To get ModifiableValueMap, just adapt resource to it
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
In my situation the property does not exist on the JCR just yet. Will ModifiableValueMap allow to add properties on the fly?
Also, Is there any particular reason for not using Node API directly?
I am aware that using it hides the resource abstraction provided by Sling but other than Sling APIs will anyways all JCR APIs behind the scene so this should work either ways.
Why use Resource API:
Simply put, Resources offer various APIs that provide default values and handle exceptions more effectively when a value or child does not exist. These APIs closely align with how AEM developers typically code, resulting in more manageable and less fragile code. You would notice benefits on level of:
For more details, please refer to thread https://experienceleaguecommunities.adobe.com/t5/adobe-experience-manager/node-vs-resource-in-aem/m-...
Multiple people have shared there thoughts here.
Examples of modifiableValueMap areavailable on the link
https://aem-ti2fap.blogspot.com/2021/08/use-cases-of-modifiablevaluemap-in-aem.html
To get ModifiableValueMap, just adapt resource to it
ModifiableValueMap properties = resource.adaptTo(ModifiableValueMap.class);
Before setting the property, check this
String[] val = {"test1","test2"};
if(modifiableValueMap.get("someproperty").getClass().isArray()){
modifiableValueMap.put("property", val);
} else {
modifiableValueMap.remove("property");
modifiableValueMap.put("property", val );
}
modifiableValueMap.commit();
Hi @aem_noob,
The problem is that you are trying to change single value filed into multi-value field. This will not work, and using ModifiableValueMap will not change that. You will end up with similar exception (JCR api will be called behind the scene anyway).
This is also explained in the JCR documentation:
An attempt to set a multi-value property with a non-array value argument, or a single-value property with an array value argument, will throw a ValueFormatException.
To solve the issue, you will have to first remove single value field, and next you can replace it with multi-value, so your code can look like this:
Resource resource = request.getResourceResolver().getResource(aemPath);
Node node = resource.adaptTo(Node.class);
Value[] values = new Value[ArrayList.size()];
node = resource.adaptTo(Node.class);
Value[] values = new Value[ArrayList.size()];
ValueFactory valueFactory = node.getSession().getValueFactory();
for (int index=0; index<ArrayList.size(); index++) {
values[index] = valueFactory.createValue(ArrayList.get(index));
}
// Checking if property is multi-value, if not removing property, so it can be set again but as a multi-value
Property property = node.getProperty(aemFieldName);
if (!property.isMultiple()) {
property.remove();
}
node.setProperty(aemFieldName, values);
If you will decide to refactor you code and use Sling api (ModifiableValueMap), you will have to remove property as well to make it work.
@lukasz-m- This throws a null pointer exception -
Property property = node.getProperty(aemFieldName);
aemFieldName is not existing on the JCR just yet - Its being read from JSON and then put into JCR.
When trying to do it directly via setProperty it throws value format exception
node.setProperty(aemFieldName, values);
Your code looks correct but for some reason the property being set is classified as single type even though values array is being passed to it.
One minor point I can take out is as per Adding_And_Writing_Properties
setProperty(String name, Value[] values);
The property type of the property will be that specified by the node type of this node.
If the property type of the supplied Value objects is different from that required, then a best-effort conversion is attempted
If the conversion fails, a ValueFormatException is thrown.
I am not sure what will make a forced conversion in this case. Can you try the below snip once ?
Property property = node.setProperty(aemFieldName, values);
if (!property.isMultiple()) {
property.remove();
}
@Rohan_Garg- Thanks a lot, this works
The below sets single valued property
node.setProperty(aemFieldName, values);
this sets multi-valued property.
Property property = node.setProperty(aemFieldName, values);
why is the property casting causing the difference?
Not sure exactly about that but here's my best guess -
Without casting to Property type the node.setProperty() works on best-effort conversion - This might very well be the number of properties set via above statement - If most of the properties you are setting are single lined values then stands to reason the best effort will evaluate like its a single lined value.
Try setting all multi-lined properties and use this without Property type!
@aem_noob Do you find the suggestions from users useful? Please let us know if more information is required. Otherwise, please mark the answer as correct for posterity.
Views
Likes
Replies
Views
Likes
Replies