Skip to content

Support Kotlin value class properties in SpEL #30468

Closed
@christianhujer

Description

@christianhujer

It would be great if SpringEL could be improved to support Kotlin Value Classes (probably by unmangling mangled Kotlin names).

I had the following issue.

Class:

data class Something(
    val id: UUID<Something>,
    val name: String,
)

It worked everywhere except in Thymeleaf. When I was using it in Thymeleaf, I got an exception:

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/pages/Something.html]")
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "something.id" (template: "pages/Something" - line 277, col 4)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "something.id" (template: "pages/Something" - line 277, col 4)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'id' cannot be found on object of type 'com.example.Something' - maybe not public or not valid?

Turns out, the problem was that the field id was of a type that itself is a Kotlin Value Class.

What are Kotlin Value Classes?

Kotlin Value Classes are classes that wrap other values, granting more type safety during compilation in Kotlin, without introducing the indirection overhead of a regular class.

Examples:

@JvmInline value class Latitude(val latitude: Double)
@JvmInline value class Longitude(val longitude: Double)
@JvmInline value class UUID<TargetType>(val value: java.util.UUID)

When value classes are used, the Kotlin compiler mangles the name of the context function to prevent name conflicts when overloading:

class Location {
    fun updateLocation(latitude: Double, longitude: Double)
    fun updateLocation(latitude: Latitude, longitude: Longitude)
}

The actual signature of the second updateLocation function would also be updateLocation(Double, Double), which clashes with the first definition. To prevent that, Kotlin mangles the name into something like updateLocation-hash.

This also applies to the getter for properties:

data class Location(
    val latitude: Latitude,
    val longitude: Longitude,
)

@Entity data class Something(
    @Id
    val id: UUID<Something>,
    val name: String,
)

The names for the getters of the properties will be mangled.

To work nicely in Spring, a few pieces are required:

  • Support by Jackson Serialization - done, Jackson detects and unmangles mangled names to get JSON property names without the mangling suffix. 👍
  • Support by Jackson Deserialization - open
  • Support by Hibernate - done, Hibernate detects and unmangles mangled names to create column names without the mangling suffix. 👍
  • Support by SpringEL - missing, SpringEL is unaware of Kotlin's name mangling. This is of particular interest for Thymeleaf users as Spring Thymeleaf uses SpringEL inside HTML.

Workaround: Until SpringEL supports Kotlin name mangling, explicitly override the mangled getter name, like this:

data class Location(
    @get:JvmName("getLatitude")
    val latitude: Latitude,
    @get:JvmName("getLongitude")
    val longitude: Longitude,
)

@Entity data class Something(
    @Id
    @get:JvmName("id")
    val id: UUID<Something>,
    val name: String,
)

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)theme: kotlinAn issue related to Kotlin supporttype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions