tags:

views:

343

answers:

11

Consider the following code:

Class Demo
{
    Person person = new Person("foo"); // assume Person class has a 'name' field.

    //Now, how do I get the value of a field, using some expression.
    String expression = "person.name";
}

Now, I want to evaluate the 'expression' to get the value of the 'name' attribute. Can anyone tell how it can be done?

-- Updated: the 'expression' string I'll be getting it from an XML file. So, will it be possible to use 'Reflection' in that scenario also? the 'expression' string may go into deeper levels also, like 'person.birthday.date'.

A: 

Assuming you don't want the simple route of String expression = person.name; (without the quotes around person.name), you need reflection.

Parsing an arbitrary expression isn't easy, though; if you want to call methods with parameters, you're inching towards a custom scripting language. But just to access a member, it's not too hard to do. But before I write you any code, I'd like to know why you want to have arbitrary expressions when plain Java will do here.

Edit: This code might do the trick:

public static Object getValue(Object obj, String path)
        throws NoSuchFieldException,
               IllegalArgumentException,
               IllegalAccessException {

    Object currentObj = obj;

    while (true) {
        int indexOfDot = path.indexOf('.');
        boolean lastIter = indexOfDot < 0;

        String fieldName = lastIter ? path : path.substring(0, indexOfDot);

        Field f = currentObj.getClass().getDeclaredField(fieldName);
        f.setAccessible(true);
        currentObj = f.get(currentObj);

        if (lastIter)
            return currentObj;

        path = path.substring(indexOfDot + 1);
    }
}
Michael Myers
A: 

The 'proper' way is to have a getter method in Person called getName() which returns the value of name.

talonx
I've requirement to use an expression instead of the getter method. BTW, I do have a getter method. :)
Veera
Getter method are frequently the improper way.
Tom Hawtin - tackline
+2  A: 

Typically in Java you create getters to retrieve this information. However, if you need to get the value of a field by name you should be using reflection.

Yet, there is something about your question that nags me that your problem has more to do with design than reflection. Can you tell us more?

Elijah
+2  A: 

There's no native way in Java to do this. A variety of other options exist.

One approach is to use JXPath. This uses XPath-style syntax to query objects. It may be a little overkill for your purposes, but is undoubtedly powerful.

e.g. from the JXPath home page:

Address address = (Address)JXPathContext.newContext(vendor).
         getValue("locations[address/zipCode='90210']/address");

which asks the given vendor object for it's address given a zip code of 90210. So you can navigate object hierarchies and provide predicates to perform 'SELECT .. WHERE' type queries.

See also this java.net article for more examples (shameless plug of something I wrote).

Brian Agnew
yup. I'm looking for something similar. :)
Veera
I think this covers all your requirements, in that you can specify a query string for JXPath in your XML, and you can navigate through your object hierarchy without your objects having to (say) implement particular interfaces etc.
Brian Agnew
A: 

Although you could do this with reflection (java.bean might make more sense), use of reflection tends to indicate confusion.

Why do you want some text to be interpreted? Are you intentionally writing an interpreter? Should the reference to person really be treated as an object in its own right? Tell us your intent.

Tom Hawtin - tackline
A: 

If you are looking to allow users to customize your program by giving expressions ( such as your person.name expression ) you could look at embedding groovy or jython in your program and running their expressions within that context. Here's the first google hit for embedding groovy. http://groovy.codehaus.org/Embedding+Groovy

digitaljoel
A: 

You could certainly do this sort of thing on your own by writing a Reflection-wielding parser, but you could also use Spring's Expresion Evaluation language, which is very nearly what you want (Documentation!).

You can do some cool stuff with it; it's a powerful language that resembles JSP's EL script (which is also an option). You just need to pass it a context in which "person" means something.

Example!

public class DoStuff {
  class MyVariables {
    private person;
    public void getPerson() { return person; }
    public void setPerson(Person person) { this.person = person; 
  }

  public static Object eval( String input ) {
     MyVariables myVariables = new MyVariables();
     StandardEvaluationContext simpleContext = new StandardEvaluationContext(myVariables);
     ExpressionParser parser = new SpelAntlrExpressionParser();
     Expression exp = parser.parseExpression( input );
     return exp.getValue(context);
  }

  public static void main(String[] args) {
      String name = (String)eval("person.name");//woot!
  }

}
CaptainAwesomePants
+1  A: 

You can use reflection although I recommend the use of a tool to parse the expressions like others have suggested.

But, if your expressions are simple you can do something like this:

public static Object GetFieldValue(Object obj, String expr) throws SecurityException, NoSuchFieldException, IllegalAccessException {
 if (obj == null)
  throw new IllegalArgumentException("obj");

 if (expr == null || expr.isEmpty())
  throw new IllegalArgumentException("expr");

 String[] parts = expr.split("\\.");

 if (parts.length > 0) {
  Object currentFieldValue = obj;

  for (String fieldName : parts) {
   Field field = currentFieldValue.getClass().getDeclaredField(fieldName);
   field.setAccessible(true);
   currentFieldValue = field.get(currentFieldValue);
  }

  return currentFieldValue;
 }

 return null;
}
bruno conde
A: 

If the data is an XML DOM, then XPath is probably the best option. If you want to add your own support for the Unified Expression Language, you can add it by implementing a few classes. If you want to use virtually any scripting language via javax.script (needs Java 6), have a look at this library of scripting engines.

Out of the box, you can do this with Java 6:

public class ResolveMe {

  public URI uri;

  public static void main(String[] args)
      throws ScriptException, URISyntaxException {
    ResolveMe myObject = new ResolveMe();
    myObject.uri = new URI("http://stackoverflow.com/foo");

    String expression = "myObject.uri.host";

    ScriptEngineManager sem = new ScriptEngineManager();
    ScriptEngine engine = sem
        .getEngineByMimeType("application/javascript");
    engine.put("myObject", myObject);

    System.out.println(engine.eval(expression));
  }

}
McDowell
A: 

Do you actually NEED the "Name" to be a member in a java object? Can you store it in a hashtable and use a lookup instead?

(Hint: you only NEED it to be a member of an object if you are doing operations on it inside that object, and even then you have an object store its' data internally as a hash if you wanted to)

Bill K
A: 

Don't do such hard work by yourself. Use a library that does this. Apache common's bean utils has a method BeanUtils.getProperty(Object bean, String name) that you can pass in your bean and the property to. For instance, BeanUtils.getProperty(foo, "name") will get you the name property of the Person of foo (you wouldn't use "person.name" because that would be equivalent to foo.getPerson().getName()).

You can download the BeanUtils jar from the maven central repository: http://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.0/commons-beanutils-1.8.0.jar

It may need some dependencies that are in the http://repo1.maven.org/maven2/commons-beanutils hierarchy, I'm not sure. You can google for apache commons BeanUtils jar and see if you can get the dependencies this way, but you may be able to use the jar standalone.

MetroidFan2002