3

使用 Pebble 版本 3.0.6。

我需要检查值 'v' 是否具有特定变量(翻译为 Java:如果 Object v 具有特定属性)。寻找类似的东西

{% if v instanceof test.MyClass %}
   ...
{% endif %}

或者

{% if v has myProperty %}
   ...
{% endif %}

据我所知,两者都不可用。使用 Pebble 实现这一目标的最佳方法是什么?

更新

语境:

  • 使用strictVariables=true
  • 属性不是布尔值、字符串或数字
4

1 回答 1

3

不是内置的,但 pebble 允许您编写自定义扩展。在 javainstanceof中是一个运算符,它是 pebble 允许您为其编写扩展的东西。

我们需要 3 件事来为操作员编写自定义扩展:

  1. 描述运算符 ( implements BinaryOperator)的类
  2. 描述如何评估运算符的类 ( extends BinaryExpression<Object>)
  3. 将这个运算符添加到 pebble 的二元运算符的类,这是扩展类和 should implements Extension

第1步

我们将运算符定义为instanceof优先级为30,根据java的优先级instanceof与 相同< > <= >=在 pebble 中这些运算符的优先级为 ,30因此我们使用它。评估此操作的节点是InstanceofExpression.class,这是我们将在步骤 2 中创建的类。

   public class InstanceofOperator implements BinaryOperator {

    /**
     * This precedence is set based on
     * <a href="https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html">Java
     * Operators</a> 30 is the same precedence pebble has set for operators like {@code instanceof}
     * like <a href="https://github.com/PebbleTemplates/pebble/wiki/extending-pebble">Extending
     * Pebble</a>.
     */
    public int getPrecedence() {
        return 30;
    }

    public String getSymbol() {
        return "instanceof";
    }

    public Class<? extends BinaryExpression<?>> getNodeClass() {
        return InstanceofExpression.class;
    }

    public Associativity getAssociativity() {
        return Associativity.LEFT;
    }

}

第2步

我们现在必须写出运算符的求值结果,在这种情况下,我们将返回true if left instanceof right。对于此评估的右侧部分,我们使用 a Stringwhich 必须包含类的完整限定名称,例如1 instanceof "java.lang.String"which will returnfalse1 instanceof "java.lang.Long"which will return true

如果right无法找到/加载类,则会引发异常Class.forName

public class InstanceofExpression extends BinaryExpression<Object> {

    @Override
    public Object evaluate(PebbleTemplateImpl self, EvaluationContextImpl context) {
        // The left class (left instanceof right)
        Object leftClass = getLeftExpression().evaluate(self, context);

        // The right class, this is a string with the full qualifying name of the class eg (left
        // instanceof "java.lang.String")
        String rightClassname = getRightExpression().evaluate(self, context).toString();

        // We must get the right class as Class<?> in order to check if left is an instanceof right
        Class<?> rightClass;
        try {
            rightClass = Class.forName(rightClassname);
        } catch (ClassNotFoundException e) {
            throw new PebbleException(e.getCause(),
                    String.format("Cannot find class %s", rightClassname));
        }

        // Check if the left class is an instanceof the right class
        return rightClass.isInstance(leftClass);
    }

}

第 3 步

我们现在必须为 Pebble 创建一个扩展,这很简单。我们创建一个自定义实例InstanceofOperator并将其作为二元运算符返回:

public class InstanceofExtension implements Extension {

    @Override
    public List<BinaryOperator> getBinaryOperators() {
        return Arrays.asList(new InstanceofOperator());
    }

    // ...
    // Other methods required by implementing Extension, these other methods can just return null.
    // ...
    // ...

}

或者,您可以这样实现该方法,而不是整个步骤 1 :getBinaryOperators

@Override
public List<BinaryOperator> getBinaryOperators() {
  return Arrays.asList(new BinaryOperatorImpl("instanceof", 30, InstanceofExpression.class,
            Associativity.LEFT));
}

利润!

我们现在可以添加我们的自定义扩展.extension(new InstanceofExtension())

PebbleEngine engine =
        new PebbleEngine.Builder().strictVariables(true)
                .extension(new InstanceofExtension()).build();

PebbleTemplate compiledTemplate = engine.getTemplate("home.html");

// Test with Person as v
Writer personWriter = new StringWriter();

Map<String, Object> context = new HashMap<>();
context.put("v", new Person());
compiledTemplate.evaluate(personWriter, context);

System.out.println(personWriter.toString()); // <b>asdasdasdasds</b> is present

// Test with Fruit as v
Writer fruitWriter = new StringWriter();

context.put("v", new Fruit());
compiledTemplate.evaluate(fruitWriter, context);

System.out.println(fruitWriter.toString()); // <b>asdasdasdasds</b> is not present, but
                                            // <b>red</b> is

我们在上面处理的Person类被定义为可以扩展Entity。为了证明这个概念有效,我们还有一个Fruit不扩展的类Entity。我们测试这两个不同的类v

class Person extends Entity {
    public String name = "me";

}

class Entity {
    public String asd = "asdasdasdasds";

}

class Fruit {
    public String color = "red";
}

home.html我们检查vwhich 是Person or 是orFruit的一个实例:com.mypackage.test.Entitycom.mypackage.test.Fruit

<html>
    <body>
        {% if v instanceof "com.mypackage.test.Entity" %}
            <b>{{ v.asd }}</b>
        {% endif %}
        {% if v instanceof "com.mypackage.test.Fruit" %}
            <b>{{ v.color }}</b>
        {% endif %}
    </body>
</html>

输出是:

<html>
    <body>
        <b>asdasdasdasds</b>
    </body>
</html>
<html>
    <body>
        <b>red</b>
    </body>
</html>

评论

“left not instanceof right”版本是:

{% if not (v instanceof "com.mypackage.test.entity") %}
于 2018-12-20T09:44:32.547 回答