Accessing an Object's attribute value using an expression

Accessing an Object's attribute value using an expression

流心雨 发布于 2021-11-27 字数 627 浏览 710 回复 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'.

如果你对这篇文章有疑问,欢迎到本站 社区 发帖提问或使用手Q扫描下方二维码加群参与讨论,获取更多帮助。

扫码加入群聊

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

作死小能手 2022-06-07 11 楼

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.

情愿 2022-06-07 10 楼

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)

街道布景 2022-06-07 9 楼

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));
  }

}

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);
    }
}
墨小墨 2022-06-07 7 楼

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

绝不服输 2022-06-07 6 楼

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.

找个人就嫁了吧 2022-06-07 5 楼

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

面犯桃花 2022-06-07 4 楼

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;
}
残花月 2022-06-07 3 楼

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!
  }

}
小瓶盖 2022-06-07 2 楼

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?

爱冒险 2022-06-07 1 楼

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).