简体   繁体   中英

Java EL expressions: “property (num1|str) not readable on type (int|String)”

I am trying to use EL expressions to create a predicate for filtering arbitrary objects using provided strings to describe the filter. It works for primitive types, but for some reason it tries to access the POJOs I supply as if they were the type they were trying to receive.

For example, the filter: "item.num1 > item.num2" applied to the POJO:

 private class TestClass {
        private String str;
        private int num1, num2;

        public String getStr() {
            return str;
        }

        public void setStr(String str) {
            this.str = str;
        }

        // getters/setters continue below here
 }

produces this Exception:

javax.el.PropertyNotFoundException: Property 'num1' not readable on type int
    at javax.el.BeanELResolver$BeanProperty.read(BeanELResolver.java:267)
    at javax.el.BeanELResolver$BeanProperty.access$000(BeanELResolver.java:216)
    at javax.el.BeanELResolver.getValue(BeanELResolver.java:62)
    at javax.el.CompositeELResolver.getValue(CompositeELResolver.java:55)
    at org.apache.el.parser.AstValue.getValue(AstValue.java:183)
    at org.apache.el.parser.AstGreaterThan.getValue(AstGreaterThan.java:38)
    at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:185)
    at com.<redacted>.filters.ELPredicate.apply(ELPredicate.java:78)
    at com.google.common.collect.Iterators$7.computeNext(Unknown Source)
    at com.google.common.collect.AbstractIterator.tryToComputeNext(Unknown Source)
    at com.google.common.collect.AbstractIterator.hasNext(Unknown Source)
    at com.google.common.collect.Lists.newArrayList(Unknown Source)
    at com.google.common.collect.Lists.newArrayList(Unknown Source)
    at com.<redacted>.common.el.ELPredicateTest.pojoTest(ELPredicateTest.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

The predicate I put together is this:

public class ELPredicate<T extends Object> implements Predicate<T> {
    private String expression;
    private String radix;

    /**
     * @param expression
     *            expression
     * @param radix
     *            string to be replaced with value in expression
     */
    public ELPredicate(String expression, String radix) {
        this.expression = String.format("${%s}", expression);
        this.radix = radix;
    }

    @Override
    public boolean apply(final T input) {
        final Map<String, T> map = ImmutableMap.of(radix, input);
        ELContext context = new ELContext() {

            @Override
            public VariableMapper getVariableMapper() {
                // TODO Auto-generated method stub
                return null;
            }

            @Override
            public FunctionMapper getFunctionMapper() {
                // TODO Auto-generated method stub
                return null;
            }

            @Override
            public ELResolver getELResolver() {
                return new CompositeELResolver() {
                    {
                        this.add(new AttributeELResolver((Map<String, Object>) map));
                        this.add(new BeanELResolver(true));
                        this.add(new MapELResolver(true));
                        this.add(new ListELResolver(true));
                        this.add(new ArrayELResolver(true));

                    }
                };
            }
        };

        return (boolean) ExpressionFactory.newInstance().createValueExpression(context, expression, boolean.class).getValue(context);
    }

}

The AttributeELResolver is an in-house piece of code that I can't paste here, but it just resolves strings in the expression to their values in the map, it lets me define what 'item' is.

Found the solution!

As it turns out, it was an access issue, not a type compatibility issue like I first thought. Changing the private inner class (TestClass) to public made everything work as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM