使用表达式访问Object的属性值

时间:2009-04-27 15:29:40

标签: java

请考虑以下代码:

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

现在,我想评估'表达式'以获取'name'属性的值。任何人都可以告诉我们该怎么做?

- 更新:'表达式'字符串我将从XML文件中获取它。那么,是否可以在该场景中使用“反射”? 'expression'字符串也可以进入更深层次,比如'person.birthday.date'。

11 个答案:

答案 0 :(得分:3)

Java中没有本地方法可以做到这一点。存在各种其他选择。

一种方法是使用JXPath。这使用XPath样式的语法来查询对象。对你的目的来说,这可能有点矫枉过正,但无疑是有力的。

e.g。来自JXPath主页:

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

在给定邮政编码为90210的情况下询问给定供应商对象的地址。因此,您可以导航对象层次结构并提供谓词以执行“SELECT .. WHERE”类型查询。

有关更多示例,请参阅this java.net文章(我写的东西的无耻插件)。

答案 1 :(得分:2)

通常在Java中,您可以创建getter来检索此信息。但是,如果您需要按名称获取字段的值,则应使用reflection

然而,你的问题有些让我觉得你的问题更多地与设计而不是反思有关。你能告诉我们更多吗?

答案 2 :(得分:1)

你可以通过编写一个反射式解析器来自己做这类事情,但你也可以使用Spring的Expresion Evaluation语言,这几乎就是你想要的(Documentation!)。

你可以用它做一些很酷的事情;它是一种强大的语言,类似于JSP的EL脚本(也是一种选择)。你只需要传递一个“人”意味着什么的语境。

实施例<!/ P>

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

}

答案 3 :(得分:1)

您可以使用反射,但我建议使用工具来解析其他人建议的表达式。

但是,如果您的表达式简单,您可以执行以下操作:

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

答案 4 :(得分:0)

假设您不想使用String expression = person.name;的简单路线(person.name附近没有引号),则需要reflection

解析任意表达并不容易;如果你想用参数调用方法,那么你正在寻找一种自定义脚本语言。但只是为了访问一个成员,这并不难。但是在我给你编写任何代码之前,我想知道为什么你想要普通Java在这里做任意表达式。

编辑:此代码可以解决问题:

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

答案 5 :(得分:0)

'正确'的方法是在Person中使用一个名为getName()的getter方法,它返回name的值。

答案 6 :(得分:0)

虽然你可以用反射来做到这一点(java.bean可能更有意义),但反射的使用往往表明存在混淆。

为什么要解释一些文字?你是故意写一个翻译吗?是否真的应该将person的引用视为对象?告诉我们你的意图。

答案 7 :(得分:0)

如果您希望允许用户通过提供表达式(例如您的person.name表达式)来自定义您的程序,您可以查看在程序中嵌入groovy或jython并在该上下文中运行它们的表达式。这是嵌入groovy的第一个谷歌热门。 http://groovy.codehaus.org/Embedding+Groovy

答案 8 :(得分:0)

如果数据是XML DOM,则XPath可能是最佳选择。如果您想为Unified Expression Language添加自己的支持,可以按implementing a few classes添加。如果您想通过javax.script使用几乎任何脚本语言(需要Java 6),请查看this library of scripting engines

开箱即用,您可以使用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));
  }

}

答案 9 :(得分:0)

你真的需要“Name”成为java对象的成员吗?你可以将它存储在哈希表中并使用查找吗?

(提示:如果你在对象内部对它进行操作,你只需要它成为一个对象的成员,即使这样你有一个对象存储它的'数据在内部作为哈希,如果你想的话)

答案 10 :(得分:0)

不要自己做这么辛苦的工作。使用执行此操作的库。 Apache common的bean utils有一个方法BeanUtils.getProperty(Object bean,String name),你可以将bean和属性传递给它。例如,BeanUtils.getProperty(foo,“name”)将为您提供foo的Person的name属性(您不会使用“person.name”,因为这将等同于foo.getPerson()。getName() )。

您可以从maven中央存储库下载BeanUtils jar: http://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.0/commons-beanutils-1.8.0.jar

可能需要http://repo1.maven.org/maven2/commons-beanutils层次结构中的某些依赖项,我不确定。你可以google for apache commons BeanUtils jar,看看你是否可以通过这种方式获得依赖项,但是你可以单独使用jar。