0

This simple program using OGNL returns me a list of null values instead of [A,B,D]. I've tried #root{#x.get(#this)} and still returns me a list of nulls. Why? Thanks.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


import ognl.Ognl;



public class ognlTest {
    static public void main(String[] args) throws Exception
    {
        Map<String, Object> m = new HashMap<String, Object>();

        m.put("a", "A");
        m.put("b", "B");
        m.put("c", "C");
        m.put("d", "D");

        List<String> k = new ArrayList<>();
        k.add("a");
        k.add("b");
        k.add("d");


        Map<String, Object> context = new HashMap<String, Object>();
        context.put("x", m);

        Object expression = Ognl.parseExpression( "#root.{ #x[#this] }");

        Object value = Ognl.getValue( expression, context, k);

        System.out.println("value = [" + value + "]");

    }
}

Update:

Use #root.{#z=#this, #x[#z]}. Silly but it works. I believe this is a bug but Apache OGNL 4 is in limbo and OGNL 3.0.6 is ... I'm not sure it is maintained; the distribution doesn't even compile because it has wrong dependencies.

Aleksandr M
  • 24,264
  • 12
  • 69
  • 143
Apprentice Queue
  • 2,036
  • 13
  • 13
  • Could you tell what you are trying to achieve with that expression? Maybe be it can be done in a different way. Anyway for the explanation why it returns nulls see my answer. – Aleksandr M Sep 20 '13 at 21:08
  • It should return [A,B,D]. Given an array of keys, give me back a list of values from a map. – Apprentice Queue Sep 21 '13 at 21:12
  • Yes, but why you are doing it like that? Why use projection over root object and not over map? Any reason for that? – Aleksandr M Sep 22 '13 at 18:31
  • Updated my answer with HOW to select from map with list as keys. – Aleksandr M Sep 22 '13 at 22:16
  • This `#root.{#z=#this, #x[#z]}` confirms that `#this` is out of scope when used inside `{#x[]}`. Not a bug. – Aleksandr M Oct 31 '13 at 18:21
  • @Aleksandr M, `{}` is the projection operator; it should change `#this` to something and not refer to `root`. That is why `#root.{#this.length()}` works as expected: it traverses the list and applies `length()` to each element in the list. – Apprentice Queue Nov 01 '13 at 00:32
  • Correct. But think about it, OGNL first will evaluate expression inside `#x[...]` and after that comes projection evaluation. So `#this` inside `[]` cannot refer to *projection #this*. – Aleksandr M Nov 01 '13 at 08:21
  • @Aleksandr M, I understand what you are saying but `[]` should not start a new context causing `this` to change. I believe this is a bug. – Apprentice Queue Nov 01 '13 at 19:19
  • If you have inner class and refer to `this` in it then you are refering to inner class not parent class. – Aleksandr M Nov 01 '13 at 20:48
  • @Aleksandr M, in Java you do not want `obj.doSomething(2*this.x)` for `this` to refer to `obj`. The plain and simple requirement is `[]` should not start a new context. It is a very silly usage and if you really did mean `this` to refer to `x`, you can easily do so with `#x[#x]`! – Apprentice Queue Nov 01 '13 at 20:50
  • :) but projection isn't like calling a method, it is more like a query. From the docs: *We call this "projection," from the database term for choosing a subset of columns from a table.* And this in `[]` will not refer to `x` but to the root (in this case `k`). – Aleksandr M Nov 01 '13 at 20:54
  • @Aleksandr M, I'm sorry but this "query" and "method" distinction is not valid. Nothing gets done in Java without a method call. You can continue to think this is a feature and I'll continue to believe it is a bug. – Apprentice Queue Nov 01 '13 at 20:58
  • Of course we can ;). Consider this `query = #x[#this]; obj.projection(query);`. `#this` is a root (obj in this example), isn't it? – Aleksandr M Nov 01 '13 at 21:06

1 Answers1

2

First of all it is always better to indicate type of key when getting values from a map. For example by constructing a new String.

map[new java.lang.String(#this)]

That said, this expression returns nulls because #this in this context refers to root object.

You can easily verify this by creating your k list before map and putting k.toString() as a key.

m.put(k.toString(), "k_is_here");

Object expression = Ognl
                   .parseExpression("#root.{ #x[new java.lang.String(#this)] }");

The output will be:

value = [[k_is_here, k_is_here, k_is_here]]

Update

For selecting values from map with list values passed as keys use this expression:

#x.entrySet().{? #this.key in #root}.{#this.value}

First it will select from entrySet of map all entries which key is in the list and then gets values of selected entries.

Aleksandr M
  • 24,264
  • 12
  • 69
  • 143
  • Why should #this be the root object when #this is supposed reference each element of #root instead? As I understand it, #this refers to each element of a collection when used with selection operator `.{}`. For example, #root.{ #this.length() } will return [1,1,1] not [3,3,3]. – Apprentice Queue Sep 21 '13 at 20:58
  • Also I'm not convinced I need to pass a new String into a map that I already know uses String keys. – Apprentice Queue Sep 21 '13 at 21:04
  • @ApprenticeQueue: Have you tried the code I've posted? It is clearly seen that `#this` in this context inside `[]` reference root object. – Aleksandr M Sep 22 '13 at 18:33
  • @ApprenticeQueue: As for the String key, consider map where key is a String and one of the keys is `1`. When you try to get a value from that map and you pass `1` how OGNL suppose to know is it String or integer? – Aleksandr M Sep 22 '13 at 18:35
  • I did run your code but your explanation does not explain why `#this` doesn't work the way you explain it in more typical contexts such as `#root.{#this.length()}`. OGNL understands the difference between 1 and "1". For "1" you pass in `\"1\"`. – Apprentice Queue Sep 23 '13 at 01:14
  • @ApprenticeQueue: Yes, but if you pass a variable then OGNL cannot always determine it type. My guess is `#this` inside `#x[#this]` is out of projection scope. So it is global scope and `#this` returns global this which is root object. – Aleksandr M Sep 23 '13 at 07:58
  • If I pass in an Object of the wrong type, then it is good that I get Exception for it. No need to try to force a cast. If `#this` is losing scope because of `[` then it is a bug. – Apprentice Queue Sep 25 '13 at 11:59
  • 1
    @ApprenticeQueue: Not sure it is a bug. Because scope is different. It is like using this in java class. But maybe, maybe. Anyway this answer answers you question and provides way to do what you want, accept/upvote it. – Aleksandr M Sep 25 '13 at 12:02