spring-elspring-expression-languagespring-expression

How to add an object without a constructor to a list using Spring Expression Language


I want to add a BigDecimal to a list using Spring Expression Language.

public class SpelTest {

    public List<BigDecimal> values;
    
    StandardEvaluationContext context;
    SpelExpressionParser parser;

    @Before
    public void setup() {
        values = new ArrayList<>();
        context = new StandardEvaluationContext(this);
        parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
    }

    @Test
    public void shouldChangeValue() {
        values.add(BigDecimal.ONE);

        parser.parseExpression("values[0]").setValue(context, "123.4");

        assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // passes
    }

    @Test
    public void shouldAddValue() {
        parser.parseExpression("values[0]").setValue(context, "123.4");

        assertThat(values.get(0)).isEqualTo(BigDecimal.valueOf(123.4)); // fails
    }
}

Changing the first entry passes but adding an entry fails with

Caused by: java.lang.NoSuchMethodException: java.math.BigDecimal.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3349)
    at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2553)
    at org.springframework.util.ReflectionUtils.accessibleConstructor(ReflectionUtils.java:185)
    at org.springframework.expression.spel.ast.Indexer$CollectionIndexingValueRef.growCollectionIfNecessary(Indexer.java:715)
    ... 55 more

Not sure why SpEL isn't able to properly initialize a BigDecimal when the list is empty. Surprisingly I found nothing about this problem.

Thanks for helping!


Solution

  • You could avoid this problem by setting the whole list instead of single (not initialized) elements. Instead of

    parser.parseExpression("values[0]").setValue(context, "123.4");
    

    use:

    parser.parseExpression("values").setValue(context, "123.4");
    

    This also works for multiple elements, quite neat:

    parser.parseExpression("values").setValue(context, "123.4, 456.7");