如何检查 Pebble 模板中是否定义了变量?
How to check if a variable is defined in Pebble Templates?
使用 Pebble 版本 3.0.6。
我需要检查值 'v' 是否有一个特定的变量(转换为 Java:如果对象 v 有一个特定的 属性)。正在寻找类似
的内容
{% if v instanceof test.MyClass %}
...
{% endif %}
或
{% if v has myProperty %}
...
{% endif %}
据我所知,两者都不可用。使用 Pebble 实现此目标的最佳方法是什么?
更新
上下文:
- 使用
strictVariables
= true
- 属性 不是布尔值、字符串或数字
不是内置的,但 pebble 允许您编写 custom extensions。在 java instanceof
中是一个运算符,pebble 允许您为其编写扩展。
我们需要 3 件事来为运算符编写自定义扩展:
- A class 描述运算符 (
implements BinaryOperator
)
- A class 描述运算符的计算方式 (
extends BinaryExpression<Object>
)
- A class 将此运算符添加到 pebble 的二元运算符中,这是扩展 class 并且应该
implements Extension
.
步骤 1
我们将运算符定义为 instanceof
,优先级为 30
,根据 java the precedence of instanceof
is the same as < > <= >=
, in pebble,这些运算符的优先级为 30
,因此我们使用它。评估此操作的节点是 InstanceofExpression.class
,这是我们将在步骤 2 中创建的 class。
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 步
我们现在必须写出运算符的计算结果,在本例中我们将 return true
if left instanceof right
。对于此评估的右侧部分,我们使用 String
,它必须包含 class 的完整限定名称,例如 1 instanceof "java.lang.String"
将 return false
,或 1 instanceof "java.lang.Long"
将 return true
.
如果 right
class 不能 found/loaded 与 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
和 return 的实例,作为二元运算符:
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.
// ...
// ...
}
或者,您可以这样实现 getBinaryOperators
方法,而不是整个 步骤 1:
@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
class定义为扩展Entity
。为了证明这个概念有效,我们还有一个 class Fruit
没有扩展 Entity
。我们在 v
:
中测试了这两种不同的 classes
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") %}
使用 Pebble 版本 3.0.6。
我需要检查值 'v' 是否有一个特定的变量(转换为 Java:如果对象 v 有一个特定的 属性)。正在寻找类似
的内容{% if v instanceof test.MyClass %}
...
{% endif %}
或
{% if v has myProperty %}
...
{% endif %}
据我所知,两者都不可用。使用 Pebble 实现此目标的最佳方法是什么?
更新
上下文:
- 使用
strictVariables
=true
- 属性 不是布尔值、字符串或数字
不是内置的,但 pebble 允许您编写 custom extensions。在 java instanceof
中是一个运算符,pebble 允许您为其编写扩展。
我们需要 3 件事来为运算符编写自定义扩展:
- A class 描述运算符 (
implements BinaryOperator
) - A class 描述运算符的计算方式 (
extends BinaryExpression<Object>
) - A class 将此运算符添加到 pebble 的二元运算符中,这是扩展 class 并且应该
implements Extension
.
步骤 1
我们将运算符定义为 instanceof
,优先级为 30
,根据 java the precedence of instanceof
is the same as < > <= >=
, in pebble,这些运算符的优先级为 30
,因此我们使用它。评估此操作的节点是 InstanceofExpression.class
,这是我们将在步骤 2 中创建的 class。
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 步
我们现在必须写出运算符的计算结果,在本例中我们将 return true
if left instanceof right
。对于此评估的右侧部分,我们使用 String
,它必须包含 class 的完整限定名称,例如 1 instanceof "java.lang.String"
将 return false
,或 1 instanceof "java.lang.Long"
将 return true
.
如果 right
class 不能 found/loaded 与 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
和 return 的实例,作为二元运算符:
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.
// ...
// ...
}
或者,您可以这样实现 getBinaryOperators
方法,而不是整个 步骤 1:
@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
class定义为扩展Entity
。为了证明这个概念有效,我们还有一个 class 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") %}