Announcement Announcement Module
Collapse
No announcement yet.
Spring EL: Wrong context (#this) object in Indexer when used inside a projection Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring EL: Wrong context (#this) object in Indexer when used inside a projection

    Consider the following scenario:
    Code:
    Map<String, String> keyToValue = new HashMap<String, String>();
    keyToValue.put("A001", "Mr.");
    keyToValue.put("A002", "Mrs.");
    
    List<Person> persons = new ArrayList<Person>();
    persons.add(new Person("A001", "Smith"));
    persons.add(new Person("A002", "Doe"));
    Given Person is a POJO containing the properties key and name, and given keyToValue and persons are made available in an expression context as variables, the following expression works fine and gives the expected results 'A001 Smith' and 'A002 Doe':
    Code:
    #persons.![ key + ' ' + name ]
    However, the following expressions will not work as I expected although I would have expected them to result in 'Mr. Smith', 'Mrs. Doe'. The first one leads to 'null Smith' and 'null Doe', and the second one leads to "Field or property 'key' cannot be found on ROOT".
    Code:
    #persons.![ #keyToValue[ key ] + ' ' + name ]
    Code:
    #persons.![ #keyToValue[ #this.key ] + ' ' + name ]
    I've been digging into the Indexer class and found two sections that justify themselves with a comment which I believe may base on false assumptions which lead to this behaviour.

    Section one (line 381):
    Code:
    // This first part of the if clause prevents a 'double dereference' of
    // the property (SPR-5847)
    if (targetObject instanceof Map && (this.children[0] instanceof PropertyOrFieldReference)) {
    	PropertyOrFieldReference reference = (PropertyOrFieldReference) this.children[0];
    	index = reference.getName();
    	indexValue = new TypedValue(index);
    }
    This basically ensures that maps can be accessed with literal keys without the need to quote the key (i.e. #keyToValue[key] being equal to #keyToValue['key']). I guess this is a feature, not mentioned in the SpEL documentation itself but being used widely within other examples in the Spring documentation. That is why I tried getting the key to resolve against #this, which is prevented by the second section.

    Section two (line 389):
    Code:
    // In case the map key is unqualified, we want it evaluated against
    // the root object so temporarily push that on whilst evaluating the key
    try {
    	state.pushActiveContextObject(state.getRootContextObject());
    	indexValue = this.children[0].getValueInternal(state);
    	index = indexValue.getValue();
    }
    finally {
    	state.popActiveContextObject();
    }
    Here I can't imagine why it is wanted that map keys should always evaluate against the root object of the expression context. In my opinion, it should be evaluated against the parent object (since the current object is the map being indexed). Temporarily pushing the parent object (contextObjects[size-1] instead of root context object) would solve this issue.

    Any contrary opinions on this or should I file a bug?
Working...
X