Как проверить, определена ли переменная в шаблонах Pebble?
Использование Pebble версии 3.0.6.
Мне нужно проверить, имеет ли значение 'v' определенную переменную (в переводе на Java: если Object v имеет определенное свойство). Ищу что-то вроде
{% if v instanceof test.MyClass %}
...
{% endif %}
или же
{% if v has myProperty %}
...
{% endif %}
Оба не доступны, насколько я знаю. Каков наилучший способ достичь этого с Pebble?
ОБНОВИТЬ
Контекст:
- С помощью
strictVariables
знак равноtrue
- свойство не является логическим значением, строкой или числом
1 ответ
Не встроенный, а Pebble позволяет писать собственные расширения. В яве instanceof
является оператором, который является чем-то, что pebble позволяет вам написать расширение для.
Нам нужно 3 вещи, чтобы написать собственное расширение для оператора:
- Класс, который описывает оператор (
implements BinaryOperator
) - Класс, который описывает, как оценивается оператор (
extends BinaryExpression<Object>
) - Класс, который добавляет этот оператор к бинарным операторам pebble, это класс расширения и должен
implements Extension
,
Шаг 1
Мы определяем оператор как instanceof
с приоритетом 30
в соответствии с java приоритет instanceof
такой же как < > <= >=
в гальке эти операторы имеют приоритет 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
если left instanceof right
, Для правильной части этой оценки мы используем String
который должен содержать полное квалификационное имя для класса, например 1 instanceof "java.lang.String"
который вернется false
, или же 1 instanceof "java.lang.Long"
который вернется 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 мы проверяем, если v
который Person
или же Fruit
является примером com.mypackage.test.Entity
или же com.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") %}