2

I am trying to use Thymeleaf view in conjunction with Scala. I have User entity with a collection of emails and I want to access those emails in Thymeleaf template:

in User.scala

case class User (@BeanProperty val emails: scala.immutable.List[String])

in some-thymeleaf-template.html

<div th:text="${user.emails[0]}">...</div>

The problem is that Spring expression evaluator (to which Thymeleaf delegates the expression evaluation job) only understands Java collection types and thus the attempt to take the 1st element ([0]) of the Scala list (user.emails) fails with an exception.

I tried to dig into the SpEL source code to find a place where I can possibly add a Java-to-Scala conversion. And it seems like if I could intercept the call to the org.springframework.expression.PropertyAccessor.read(..) method and perform the conversions on the returning from the method it would probably work.

But I couldn't find a way how to intercept those calls. I thought about wrapping those property accessor instances with a proxy, but their initialization code is hardcoded into Thymeleaf's SpelExpressionEvaluator, SpelVariableExpressionEvaluator and SpelEvaluationContext'classes, so that there is no obvious place for me to put my wrapper in.

Is there any more or less elegant way to wrap the property accessors used in Thymeleaf (or more generally add Scala collections support to Thymeleaf and/or SpEL) without rewriting too much Thymeleaf and SpEL guts?

Thank you in advance!

Beryllium
  • 12,808
  • 10
  • 56
  • 86
Alex Vayda
  • 6,154
  • 5
  • 34
  • 50

1 Answers1

1

You could remove @BeanProperty, and add a getter for Interoperability:

case class User(emails: List[String]) {
  def getEmails = JavaConversions.asJavaIterable(emails)
}

Far more elegant is to use a Scala EL resolver

Update

As for the resolver: Somewhere in Thymeleaf/SpringEL, an expression parser resolves properties of beans: So in your example:

${user.emails[0]}

it possibly uses reflection to see if there is a getter method getEmails in the user object. This is the place where you could add the same code as in the linked Scala EL resolver: If the getter returns a Scala collection, wrap it in a Java iterable (or convert it to an array) before.

Community
  • 1
  • 1
Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • Replacing @BeanProperty with Java friendly getters - that's what I ended up doing so far. It's Ok as a workaround, but your suggestion regarding `ELResolver` is really interesting. Does Spring EL or Thymeleaf use ELResolver? If so how can I register a custom resolver? – Alex Vayda Jul 06 '13 at 00:28
  • Yes, that's what I was trying to do. There is `PropertyAccessor` interface which method 'read(..)' I wanted to intercept. But I just wasn't able to find a way how to intercept it. Usually those accessors are instantiated with 'new' and used directly, so I would probably have to rewrite a good half of the SpEL parser in order to inject my interceptor :) But anyway, since it looks like there is no better option I'd better stick with adding Java-friendly getters to my case classes until Spring guys make SpEL parser more extensible. – Alex Vayda Jul 06 '13 at 21:39