英文:
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's 2020 and Thymeleaf still [doesn't support `Optional`][1] from Java 8, released six year ago. What are the workarounds?
I'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<String>`. So I can put this in my template:
```html
[[${foo.findName()}]]
Assuming the name is "bar"
, 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("_____")}]]
Unfortunately that produces an exception:
> org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: ""
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()} : '_____']]
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()} ?: '_____'
will just work naturally.)
答案1
得分: 3
因为Thymeleaf在与Spring集成时使用了Spring表达式语言,所以Thymeleaf本身并不应受责备。在当前版本的Spring中,SpEl尚不支持Java 8的Optional,这里是GitHub问题#20433。
可能的解决方法需要不同的表达式:
-
当你有一个默认值时(通过评论解决了
_
转义问题)<span th:text="${foo.findName().orElse('---')}" />
-
当你不想渲染空(或默认)行时,可以使用
th:if
进行隐藏:<th:block th:if="${foo.findName().isPresent()}"> <span th:text="${foo.findName().get()}" /> </th:block>
英文:
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)<span th:text="${foo.findName().orElse('---')" />
-
When you don't want to render an enpty (or default) line, you can hide it using
th:if
:<th:block th:if="${foo.findName().isPresent()}"> <span th:text="${foo.findName().get()" /> </th:block>`
答案2
得分: 1
以下是翻译好的内容:
唯一能够正常工作的部分是这个:
```html
<th:block th:switch="${foo.findName().isPresent()}">
<th:block th:case="true">[[${foo.findName.get()}]]</th:block>
<th:block th:case="*">_____</th:block>
</th:block>
太啰嗦了!难道这就是最好的方式吗?
<details>
<summary>英文:</summary>
The only thing I can get to work so far is this:
```html
<th:block th:switch="${foo.findName().isPresent()}">
<th:block th:case="true">[[${foo.findName.get()}]]</th:block>
<th:block th:case="*">_____</th:block>
</th:block>
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?:"alternativeContent"
That's equivalent to this in Java:
myOptional.map(ObjectType::getMyProperty).orElse("alternativeContent");
Easier might be, not to pass Optionals to Thymeleaf alltogether and only use the Operators mentioned above:
myObject?.myProperty
myObject?.myProperty?:"alternativeContent"
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论