Java: как оценить выражение EL - автономно (вне любой веб-структуры) без реализации интерфейсов?

Я хочу использовать EL в своем приложении. Но я не могу найти как. Обычно мне нужен какой-то интерфейс, для которого у меня нет реализации.

У меня есть карта объектов, и я хочу, чтобы строковое выражение типа Hello, ${person.name} было преобразовано в строку.

Как я могу добиться этого, используя любой из Commons EL, javax.el, OGNL или тому подобное? Должна быть автономная библиотека.

И я знаю Java: использование EL вне J2EE, и вы видели JSTL / JSP EL (язык выражений) в контексте, отличном от JSP (автономном). Это не то, что я ищу.

Я ищу пример того, какую зависимость добавить, а затем как инициализировать парсер, который будет иметь:

private static String evaluateEL( String expr, Map<String, String> properties );

и позвольте мне сделать:

String greet = evaluateEL("Hello ${person.name}", 
     new HashMap(){{
       put("person", new Person("Ondra"));
     }}
);

И мне нужно использовать какое-то рациональное значение, например "" на null вместо того, чтобы бросать NPE или около того.


person Ondra Žižka    schedule 10.06.2013    source источник
comment
Хотя FreeMarker не совсем EL, он имеет аналогичный синтаксис и набор функций.   -  person fge    schedule 10.06.2013
comment
Также Velocity имеет синтаксис, аналогичный EL.   -  person Uwe Plonus    schedule 10.06.2013
comment
Возможно, это OGNL, но на их веб-сайте не работает банкомат.   -  person Ondra Žižka    schedule 10.06.2013


Ответы (2)


Существует довольно много движков EL, большинство из которых реализуют API языка выражений Java.

  • Commons EL (http://jakarta.apache.org/commons/el/) Реализация JSP EL API, который существовал вечно. Эту библиотеку можно найти во многих контейнерах JSP (например, Tomcat) или использовать в качестве основы для серверов J2EE многих поставщиков.

  • OGNL (http://commons.apache.org/proper/commons-ognl/) Один из самых выразительных EL, доступных сегодня и широко используемый с WebWork (Struts 2) и Tapestry.

  • MVEL (https://github.com/mvel/mvel) Новичок в EL, который является частью проект MVFlex / Valhalla. Функции выглядят больше в соответствии с предложением OGNL с вызовом методов и некоторой интересной поддержкой регулярных выражений.

  • (Унифицированный) язык выражений (https://jcp.org/aboutJava/communityprocess/final/jsr341/index.html и http://jcp.org/en/jsr/detail?id=245) Стандартный язык выражений, впервые представленный в Java EE 5 (EL 2.1) и улучшенный в Java EE 6 (EL 2.2) и Java EE 7 (EL 3.0). Эталонная реализация доступна в проекте Glassfish - Unified Expression Language.

  • JEXL (http://jakarta.apache.org/commons/jexl/) Реализация на основе парсера Velocity. Из-за этого он действует больше как ограниченное решение для создания шаблонов с такими вещами, как вызов метода.

Источник

На данный момент я использовал этот код с помощью BeanUtils - некрасиво, но работает.

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;

public static class SimpleEvaluator implements IExprLangEvaluator {
    private static final org.slf4j.Logger log = LoggerFactory.getLogger( SimpleEvaluator.class );

    @Override
    public String evaluateEL( String template, Map<String, String> properties ) {

        StringTokenizer st = new StringTokenizer( template );
        String text = st.nextToken("${");
        StringBuilder sb = new StringBuilder();

        // Parse the template: "Hello ${person.name} ${person.surname}, ${person.age}!"
        do{
            try {
                sb.append(text);
                if( ! st.hasMoreTokens() )
                    break;

                // "${foo.bar[a]"
                String expr  = st.nextToken("}");
                // "foo.bar[a].baz"
                expr = expr.substring(2);
                // "foo"
                String var = StringUtils.substringBefore( expr, ".");

                Object subject = properties.get( var );

                // "bar[a].baz"
                String propPath = StringUtils.substringAfter( expr, ".");

                sb.append( resolveProperty( subject, propPath ) );

                text = st.nextToken("${");
                text = text.substring(1);
            } catch( NoSuchElementException ex ){
                // Unclosed ${
                log.warn("Unclosed ${ expression, missing } : " + template);
            }
        } while( true );

        return sb.toString();
    }

    // BeanUtils
    private String resolveProperty( Object subject, String propPath ) {
        if( subject == null ) return "";

        if( propPath == null || propPath.isEmpty() ) return subject.toString();

        try {
            return "" + PropertyUtils.getProperty( subject, propPath );
        } catch(     IllegalAccessException | InvocationTargetException | NoSuchMethodException ex ) {
            log.warn("Failed resolving '" + propPath + "' on " + subject + ":\n    " + ex.getMessage(), ex);
            return "";
        }
    }

}// class SimpleEvaluator
person Ondra Žižka    schedule 11.06.2013

Я нашел один на http://juel.sourceforge.net/guide/start.html. Все еще не совсем 1-лайнер, но близко.

ExpressionFactory factory = new de.odysseus.el.ExpressionFactoryImpl();
de.odysseus.el.util.SimpleContext context = new de.odysseus.el.util.SimpleContext();
context.setVariable("foo", factory.createValueExpression("bar", String.class));
ValueExpression e = factory.createValueExpression(context, "Hello ${foo}!", String.class);
System.out.println(e.getValue(context)); // --> Hello, bar!

Maven deps:

    <!-- Expression language -->
    <dependency>
        <groupId>de.odysseus.juel</groupId>
        <artifactId>juel-api</artifactId>
        <version>2.2.7</version>
    </dependency>
    <dependency>
        <groupId>de.odysseus.juel</groupId>
        <artifactId>juel-impl</artifactId>
        <version>2.2.7</version>
        <type>jar</type>
    </dependency>
person Ondra Žižka    schedule 10.06.2013
comment
Используйте <artifactId>juel-impl</artifactId>, иначе у вас будет два конфликтующих EL-API. - person chris; 07.07.2013
comment
На основании комментария я изменил artifactId с juel на juel-impl, но не тестировал его. - person Ondra Žižka; 13.08.2018
comment
Я обновил версии. Похоже, что JUEL impl изменился с groupId на de.odysseus.juel. - person Ondra Žižka; 13.08.2018