问题
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.
回答1:
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.
回答2:
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.
回答3:
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'");
来源:https://stackoverflow.com/questions/28447222/how-to-assign-values-for-a-list-of-some-objects-defined-as-variable-with-spring