0

In a @PostMapping call, when a list of objects is received via the @RequestBody. And this list contains Int or Double variables, if these variables are not sent in the request body json, the variables are self-initialized to 0. Instead of this, I understand that it should return bad request

This problem does not happen with the BigDecimal for example and returns bad request with this variables, or if the body of the request is an object instead of a list.

Do you know how to solve this? is it a spring problem?

Example to reproduce the problem:

data class Animal(
    val name: String,
    val height: Double
)

@PostMapping("/animals")
suspend fun saveAnimals(
    @RequestBody request: List<Animal>
): ResponseEntity<Any> {
    println(request[0].height)
    return ResponseEntity.ok().build()
}

On the example above the print result will be 0 if the height is not sent on the request, but I expected this to return a bad request.

2 Answers2

1

Because Kotlin doesn't use primitive and wrapper object like Java. Example: int and Integer in Java. If kotlin can optimize, so it will do. So this Double will be double in built version. Ofc, if u call wrapper method, use any genetic (example List), or the attribute is nullable type in your code, Kotlin won't change to primitive double.

In summary: primitive double default value is 0.0

IDEA Java code example from data class Animal:

// Animal.java
package com.example.demo2;

import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 6, 0},
   k = 1,
   d1 = {"\u0000(\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0006\n\u0002\b\t\n\u0002\u0010\u000b\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0002\b\u0086\b\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006J\t\u0010\u000b\u001a\u00020\u0003HÆ\u0003J\t\u0010\f\u001a\u00020\u0005HÆ\u0003J\u001d\u0010\r\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u0005HÆ\u0001J\u0013\u0010\u000e\u001a\u00020\u000f2\b\u0010\u0010\u001a\u0004\u0018\u00010\u0001HÖ\u0003J\t\u0010\u0011\u001a\u00020\u0012HÖ\u0001J\t\u0010\u0013\u001a\u00020\u0003HÖ\u0001R\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\u0014"},
   d2 = {"Lcom/example/demo2/Animal;", "", "name", "", "height", "", "(Ljava/lang/String;D)V", "getHeight", "()D", "getName", "()Ljava/lang/String;", "component1", "component2", "copy", "equals", "", "other", "hashCode", "", "toString", "demo2"}
)
public final class Animal {
   @NotNull
   private final String name;
   private final double height;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final double getHeight() {
      return this.height;
   }

   public Animal(@NotNull String name, double height) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
      this.height = height;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   public final double component2() {
      return this.height;
   }

   @NotNull
   public final Animal copy(@NotNull String name, double height) {
      Intrinsics.checkNotNullParameter(name, "name");
      return new Animal(name, height);
   }

   // $FF: synthetic method
   public static Animal copy$default(Animal var0, String var1, double var2, int var4, Object var5) {
      if ((var4 & 1) != 0) {
         var1 = var0.name;
      }

      if ((var4 & 2) != 0) {
         var2 = var0.height;
      }

      return var0.copy(var1, var2);
   }

   @NotNull
   public String toString() {
      return "Animal(name=" + this.name + ", height=" + this.height + ")";
   }

   public int hashCode() {
      String var10000 = this.name;
      return (var10000 != null ? var10000.hashCode() : 0) * 31 + Double.hashCode(this.height);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Animal) {
            Animal var2 = (Animal)var1;
            if (Intrinsics.areEqual(this.name, var2.name) && Double.compare(this.height, var2.height) == 0) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}
Numichi
  • 926
  • 6
  • 14
1

In addition to my other answer, another concept:

Why not work validation via List: https://stackoverflow.com/a/35643761/2625393

Work with this:

implementation("org.springframework.boot:spring-boot-starter-validation:2.7.5")
data class ListAnimal(
    @field:Valid
    val list: List<Animal>
)

data class Animal(
    val name: String,

    @field:NotNull
    val height: Double?
)

@RestController
class Controller {

    @PostMapping("/animals")
    suspend fun saveAnimals(@RequestBody @Valid request: ListAnimal): ResponseEntity<Any> {
        println(request.list)
        return ResponseEntity.ok().build()
    }
}
POST http://localhost:8080/animals
Content-Type: application/json

{
    "list": [
        {
            "name": "name"
        }
    ]
}
Numichi
  • 926
  • 6
  • 14
  • I don't like so much because the height must be not null, and we are declaring it like nullable but it's the better approach that I see thanks! – Jordi Gomis Dec 01 '22 at 17:51