绕过 Thymeleaf 中缺乏 Optional 支持的方法是:

huangapple go评论59阅读模式
英文:

Working around lack of Optional support in Thymeleaf

问题

现在是2020年,Thymeleaf仍然不支持Java 8中于六年前发布的`Optional`。有什么解决方法?

我正在使用Spring Boot 2.3.3.RELEASE与Thymeleaf。我有一个类型为`Foo`的对象,控制器通过模型/会话提供为`foo`。它有一个返回`Optional<String>`类型的`findName()`方法。因此,我可以将它放在模板中:

```html
[[${foo.findName()}]]

假设名称是"bar",那么显示的将是:

> Optional[bar]

虽然这能用,但显然不是我想要的。

所以我尝试了在Java中会做的事情。以下代码应该只打印名称(例如foo),如果名称缺失则打印_____

[[${foo.findName().orElse("_____")}]]

不幸的是,这会产生异常:

> org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: ""

尝试使用条件,如果值存在,以下代码会提供该值:

[[${foo.findName().isPresent()} ? ${foo.findName().get()}]]

但因某些原因,以下代码会抛出异常:

[[${foo.findName().isPresent()} ? ${foo.findName().get()} : '_____']]

首先,我不确定为什么使用orElse()的示例不起作用,因为Optional实例像其他任何对象一样。其次,有人知道解决方法吗?第三,有人知道一个简洁的解决方法,不会占用三倍的代码?

(实际上,我们真正需要的是Thymeleaf的原生支持,这样${foo.findName()} ?: '_____'就能自然地工作。)


<details>
<summary>英文:</summary>

It&#39;s 2020 and Thymeleaf still [doesn&#39;t support `Optional`][1] from Java 8, released six year ago. What are the workarounds?

I&#39;m using Spring Boot 2.3.3.RELEASE with Thymeleaf. I have an object of type `Foo` that the controller provides via the model/session as `foo`. It has a method `findName()` that returns `Optional&lt;String&gt;`. So I can put this in my template:

```html
[[${foo.findName()}]]

Assuming the name is &quot;bar&quot;, that shows:

> Optional[bar]

That works, but obviously it's not what I want.

So I tried what I would do in Java. The following should print simply the name (e.g. foo), or _____ if the name is missing:

[[${foo.findName().orElse(&quot;_____&quot;)}]]

Unfortunately that produces an exception:

> org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: &quot;&quot;

Trying to use conditions, this will provide the value if it is present:

[[${foo.findName().isPresent()} ? ${foo.findName().get()}]]

But for some reason this throws an exception:

[[${foo.findName().isPresent()} ? ${foo.findName().get()} : &#39;_____&#39;]]

First, I'm not sure why the example using orElse() doesn't work, as the Optional instance is an object like any of the others. Secondly, does anyone know a workaround? Thirdly, does anyone know of a workaround that is concise and doesn't take up three times the code?

(What we really need is native support in Thymeleaf so that ${foo.findName()} ?: &#39;_____&#39; will just work naturally.)

答案1

得分: 3

因为Thymeleaf在与Spring集成时使用了Spring表达式语言,所以Thymeleaf本身并不应受责备。在当前版本的Spring中,SpEl尚不支持Java 8的Optional,这里是GitHub问题#20433

可能的解决方法需要不同的表达式:

  • 当你有一个默认值时(通过评论解决了_转义问题)

    &lt;span th:text=&quot;${foo.findName().orElse(&#39;---&#39;)}&quot; /&gt;
    
  • 当你不想渲染空(或默认)行时,可以使用th:if进行隐藏:

    &lt;th:block th:if=&quot;${foo.findName().isPresent()}&quot;&gt;
        &lt;span th:text=&quot;${foo.findName().get()}&quot; /&gt; 
    &lt;/th:block&gt;
    
英文:

Since Thymeleaf uses Spring Epression language while integrated with Spring, the Thymeleaf itself is not to blame. As of the current version of Spring, the SpEl doesn't support Java 8 Optional yet, here is the GitHub issue #20433.

Possible workarounds require different expressions:

  • When you have a default value (the _ escaping issue is resolved throught a comment)

    &lt;span th:text=&quot;${foo.findName().orElse(&#39;---&#39;)&quot; /&gt;
    
  • When you don't want to render an enpty (or default) line, you can hide it using th:if:

    &lt;th:block th:if=&quot;${foo.findName().isPresent()}&quot;&gt;
        &lt;span th:text=&quot;${foo.findName().get()&quot; /&gt; 
    &lt;/th:block&gt;`
    

答案2

得分: 1

以下是翻译好的内容:

唯一能够正常工作的部分是这个:

```html
&lt;th:block th:switch=&quot;${foo.findName().isPresent()}&quot;&gt;
  &lt;th:block th:case=&quot;true&quot;&gt;[[${foo.findName.get()}]]&lt;/th:block&gt;
  &lt;th:block th:case=&quot;*&quot;&gt;_____&lt;/th:block&gt;
&lt;/th:block&gt;

太啰嗦了!难道这就是最好的方式吗?


<details>
<summary>英文:</summary>

The only thing I can get to work so far is this:

```html
&lt;th:block th:switch=&quot;${foo.findName().isPresent()}&quot;&gt;
  &lt;th:block th:case=&quot;true&quot;&gt;[[${foo.findName.get()}]]&lt;/th:block&gt;
  &lt;th:block th:case=&quot;*&quot;&gt;_____&lt;/th:block&gt;
&lt;/th:block&gt;

Horrors—so verbose! Is that the best that can be done?

答案3

得分: 0

作为对Java Optional功能的解决方法,可以使用SpringEL的“Elvis运算符”和“安全导航运算符”:

安全导航运算符
如果myOptional为null,这将返回null而不是错误:

myOptional.orElse(null)?.myProperty

在Java中,这相当于:

myOptional.map(ObjectType::getMyProperty).orElse(null);

Elvis运算符
如果myOptional或myProperty为null,这将返回“alternativeContent”:

myOptional.orElse(null)?.myProperty?: "alternativeContent"

在Java中,这相当于:

myOptional.map(ObjectType::getMyProperty).orElse("alternativeContent");

更简单的方法可能是,根本不将Optionals传递给Thymeleaf,只使用上面提到的运算符

myObject?.myProperty
myObject?.myProperty?: "alternativeContent"
英文:

As workaround for the Java Optionals funcionality use the "Elvis Operator" and the "Safe Navigation Operator" of SpringEL:

Safe Navigation Operator:
If myOptional is null, this will return null instead of an error:

myOptional.orElse(null)?.myProperty

That's equivalent to this in Java:

myOptional.map(ObjectType::getMyProperty).orElse(null);

Elvis Operator:
If myOptional or myProperty is null, this will return "alternativeContent":

myOptional.orElse(null)?.myProperty?:&quot;alternativeContent&quot;

That's equivalent to this in Java:

myOptional.map(ObjectType::getMyProperty).orElse(&quot;alternativeContent&quot;);

Easier might be, not to pass Optionals to Thymeleaf alltogether and only use the Operators mentioned above:

myObject?.myProperty
myObject?.myProperty?:&quot;alternativeContent&quot;

huangapple
  • 本文由 发表于 2020年10月12日 00:10:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/64306235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定