23

In my Spring application that is built with Kotlin I would like to use bean validation on a data class that looks like this.

data class CustomerDto(
    @field: NotBlank
    val firstName: String,

    @field: NotBlank
    val lastName: String)

On sending a post with an empty firstName to the customer endpoint I would like to get the constraint validations but due to the fields not allowing null values I don't get the validations but rather get the following error.

"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Instantiation of [simple type, class pkg.CustomerDto] value failed for JSON property firstName due to missing (therefore NULL) value for creator parameter firstName which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class pkg.CustomerDto] value failed for JSON property firstName due to missing (therefore NULL) value for creator parameter firstName which is a non-nullable type\n at [Source: (PushbackInputStream); line: 19, column: 1] (through reference chain: pkg.CustomerDto[\"firstName\"])",
"path": "/shop/5/customer"

Is there any other option to mark the dto field as not optional and still get the constraint violations? When I mark them as being optional I have to use the !! on the not nullable fields in the code when mapping them to my entities.

Thanks.

3
  • 1
    Good question, the silence here possibly supports my impression. Imo there is no way since the successful construction of an object is a prerequisite to field validation. Nevertheless, I'd love to read an opinion of a Kotlin champ. I've got a Spring-related solution for this in mind, but I guess you want to solve it the Kotlin-way? Commented Sep 21, 2018 at 17:15
  • No, I don't have a good solution except for making everything optional or using the !! when mapping to my entities to rely on the null safety in my model. I would be interested in the Spring solution you had in mind. Commented Sep 24, 2018 at 12:28
  • After doing a little search, I guess, it might be a little harder than I've thought in the first place, however, possible. The question is, if the approaches justify the effort you need to make. First thing is to use Spring AOP, second HandlerInterceptors which let you access and modify the request before Jackson deserializes the request payload into your CustomerDTO. It's definitely not a simple solution, take a look at this question: stackoverflow.com/questions/50932518/… Commented Sep 24, 2018 at 21:44

3 Answers 3

8

I believe you are going at it the wrong way.

Kotlin's null safety operators exact purpose is to enforce you to explicitly express nullability behavior in your code in order to drastically minimize NPE, or at least make sure you knowingly caused them yourself :). In your (or any MVC like access pattern) case, you are faced with the following scenario

  • Field might be null as part of the DTO payload
  • Framework validation should disqualify the DTO if field is null.
  • If framework validation has succeeded, the DTO field is implicitly assumed to be not null.

Though it makes sense in terms of logical flow, it's actually a violation that may result in an NPE, because nothing in the model/contract guarantees these fields won't be null

Still, In java, you'd have just made that final assumption using a getter(you'd have been using a getter anyway, it's java, right?).

Well - it's no different in kotlin, if that's what you require:

data class CustomerDto(@field:NotNull 
                       @JsonProperty("firstName") private val _firstName: String? = null,
                       @field:NotNull
                       @JsonProperty("lastName") private val _lastName: String? = null) {
        val firstName get() = _firstName!!
        val lastName get() = _lastName!!
    }

(This examples assumes you are using jackson for JSON de/serialization)

While still manually forcing non-nullability using the !! operator (which is something you wanted to avoid), you are now abstracting that aspect from the rest of your codebase, getting that java-getter like behavior

Sign up to request clarification or add additional context in comments.

4 Comments

You describe the problem, but I was wondering if there was a way to have the @NotNull annotation on a Kotlin not null field. I still think it can be achieved if Spring first performs validation and then maps to the target class like what @Matt is describing but with framework support.
I do understand your problem, but I also believe the data-class layout I suggested solves this problem the "kotlin" way, because it allows you to enjoy both kotlin's null safety and spring validation (you could still annotate the DTO fields) while abstracting the former from the rest of your application and keeping it as an inner data class design consideration. I can't see how that's not enough.
field:JsonProperty must be used to recognize that annotation is for the field. The getter must have get:JsonIgnore annotation, so that jackson doesn't use the getter for identification. If it identifies and calls the getter when the hidden value is null, it will result in a JsonProcessingException due to NPE. Next, even with this, in case of a validation error, in the response, spring will show the field name, instead of the json property. It is described in this question: stackoverflow.com/questions/41717866/…
Verbosity.. We don't move on Kotlin for writing java-like code ;)
1

I think a better solution is to use Kotlin's default value inside properties:

data class CustomerDto(
@field: NotBlank
val firstName: String="",

@field: NotBlank
val lastName: String="")

First name and last name attributes will always have a value (from json or default value =""). The solution is not perfect but It works like it was expected.

1 Comment

Be aware, that default value helps only if property missed in json, like that { "firstName": "Name" }. But it still throws exception if json explicitly defines property as null, like that { "firstName": "Name", "lastName": null }
0

I'm late to the party here, but I hope it can help someone else.

Since Spring first tries to create an instance of the bean annotated with @ModelAttribute before actually trying to bind the fields (see ModelAttributeMethodProcessor), we don't have much choice. While we can use a default value for the fields, this is not an option for complex objects (e.g. MultipartFile).

Looking at the source code, I found out Spring can set those values using a DataBinder. So, we can declare the fields to be initialized later without the need to be nullable.

class MyClassDTO {

    @field:NotBlank(message = "Name cannot be blank")
    lateinit var name: String

    @field:NotBlank(message = "Topic cannot be blank")
    lateinit var topic: String

    @field:NotNull(message = "File cannot be null")
    lateinit var file: MultipartFile
}

1 Comment

lateinit looks like workaround, but it does not work with primitives. is there any better solution?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.