1

In Spel, it is easy to assign some values for a List property. For example having object foo with a property defined as List, I usually do:

SpelParserConfiguration config = new SpelParserConfiguration(true,true);
ExpressionParser parser = new SpelExpressionParser(config);
Foo foo = new Foo();
EvaluationContext context = new StandardEvaluationContext(foo);
parser.parseExpression("barList[1].test='11111111'")
    .getValue(context);

But what do you do for the case you want to assign values for a given List defined as a variable in a method. e.g:

List<String> fooList = new ArrayList<String>();
context = new StandardEvaluationContext(fooList);       
parser.parseExpression("SOMETHING[0]='come on'")
.getValue(context);

In the above example, I don't know what to put instead of SOMETHING to make this work. If I put "fooList[0]='....'", it throws an exception complaining there is no fooList property in fooList.

If I put "[0]='....'", it throws Unable to grow collection: unable to determine list element type.

Then I came to define a generic wrapper like this:

public static class SpelWrapper<T>
{
    T obj;

    public SpelWrapper(T obj)
    {
        this.obj = obj;
    }

    public T getObj() {
        return obj;
    }

    public void setObj(T obj) {
        this.obj = obj;
    }
}

and then tried to test this one:

List<String> fooList = new ArrayList<String>();
    SpelWrapper<List<String>> no = new SpelWrapper<List<String>>(fooList);
    context = new StandardEvaluationContext(no);

    parser.parseExpression("obj[0]='olaaa'")
    .getValue(context);

But it did not work and still get this ugly message:

Unable to grow collection: unable to determine list element type

I tried other expression languages like MVEL, OGNL, JEXL but I noticed they don't support auto null reference initialization which is important for me. However they did not seem to have a solution around above problem either.

I also started to think what if what I need is not a case for expression languages! The thing is my need is not just defining a variable and try to assign values using an EL.

In my case I have some simple POJO domain class beans and Some input Strings like

 "bar[0].foo.value=3434"

Now I should be able to create List of Bar and put a Bar instance as its first element, then set foo property with a Foo instance and finally set Foo's value as 3434.

Any idea around this issue?

Thanks in advance

Eit I was wrong in "However they did not seem to have a solution around above problem either". For example in MVEL, this a very easy task to do. But unfortunately the SPLE's ability to auto expanding lists and automatically assigning initiators in null chains makes it incredibly proper for my case.

madz
  • 1,803
  • 18
  • 45

4 Answers4

2

The problem is due to the Type erasure in Java, Spring or other Els don't have a clue to automatically initiate nulls in a chain for generic root objects including Lists. For example in a expression like this in SpEl:

#root[10].mySet='ohoy'

If it detects it needs to initiate the ArrayList first, there is no way of knowing what is the proper element Type to initiate at given index. Thats why it needs a wrapper to use reflection of getter's return type.

Another work through is using an Array. Because arrays component type be kept in runtime. But the catch is arrays can't be resized in runtime. So they need to be initiated with a fairly enough size to prevent index out of range.

Anyway also the Type erasure is a real pain in the ass in order to do these kind of things, seems they (applies other ELs too) still could do some efforts to make some works round this. For example

1) If the list is not empty, it is possible to get the real element's type out of it in runtime.
2) Alter the getValue in order to get the generic type passed parameter and to use it for initiating.
3) In the case of arrays, return the expanded array.
4) ...

Although implementing a good EL that promises handle all kind of initiation or expansion seems a really hard work to do, I was working to extending Ognl to achieve this regards. I started with

PropertyAccessor

and got some really promising results. However, I don't know whats wrong with apache when it comes to develop libraries. For example I don't like the way they put handlers in a Static class. I think a handler is a context matter and yet I don't know if it worth to spend some times in order to extend Ognl, But as soon as I finished the work, I would share the result.

madz
  • 1,803
  • 18
  • 45
1

You will have to define a variable in your expression and fill it with the list to treat, e.g.

context.setVariable("mylist", fooList);

Then you can access it like this:

parser.parseExpression("#mylist.set(0)='come on'").getValue(context);

The list in the variable is used by reflection; you can call methods etc. I hope this helps.

Uwe Allner
  • 3,399
  • 9
  • 35
  • 49
  • Thank you @Uwe Aliner for the answer. But I have a couple of problems with this solution. 1) Having context = new StandardEvaluationContext(); and then context.setVariable("fooList", fooList); and finally parser = new SpelExpressionParser(config); parser.parseExpression("#fooList.set(0)='ooooooo'") .getValue(context); throws this exception : setValue(ExpressionState, Object) not supported for 'class org.springframework.expression.spel.ast.MethodReference' – madz Feb 11 '15 at 17:55
  • 1
    2) I could don't define a variable and after setting the context, Just do: "#root[0]='ooooo'". The problem is it throws Cannot index into a null value. Then making the context capable of auto expanding lists (with th SpelParserConfiguration) again makes it fails with Unable to grow collection: unable to determine list element type – madz Feb 11 '15 at 18:04
  • If I expand the list in the first solution myself, the problem is solved. But because of the expressions, (sort of I can't manipulate expression in their source, They are what they are. I only can change them after I get them. for example by regex ) I wanted to see if there is a way to solve this with a straightforward EL solution and don't get my hands greasy ;) – madz Feb 11 '15 at 18:16
0

According to this answer the following should work:

parser.parseExpression("obj").getValue(context, "olaaa"); // or is it "'olaaa'"?

or even something like

parser.parseExpression("obj").getValue(context, "'olaaa', 'alooo'");
crusy
  • 1,424
  • 2
  • 25
  • 54
0

In my case it is an arraylist of an user defined object.

None of the above mentioned solutions worked.

So I am reading till the arrayList itself and not the exact path of the object, once fetched I am iterating and checking :-(

Expression parsedValueExpression3 = parser.parseExpression("arraylist.root");

Any better suggestions are appreciated.

Kusum
  • 241
  • 5
  • 20