I found a very strange issue with serialisation/deserialisation of Request Body when using a POST call.

I am using Spring Boot 2.4.4, I tried with Spring Boot 2.2.2.RELEASE as well.

If any attribute in the Model/POJO class for @RequestBody contains any attribute which has 2nd and 3rd letters in uppercase.(yes it is this specific ), then the value for this attribute will be resolved only when both these letters are in lowercase. Say we have an attribute like tESt, if I call the API with 'tESt', I will get tESt to be null in my controller, but if I have test in request body, it will be resolved as 'tESt'.

Here is a sample POJO:

public class Dummy {
    private String tESt;
}

And lets assume we have a test POST endpoint:

@PostMapping
public String test(@RequestBody Dummy dummy) {
    return dummy.getTESt();
}

Now if we call the above API with this request body:

{
  "tESt": "string"
}

We get null in response. But if we use this:

{
  "test": "string"
}

We get the expected response.

Interestingly, when i checked the swagger-ui for my control, the default example for API is:

{
  "test": "string"
}

Swagger-ui version: 2.9.2

Comment From: wilkinsona

This is Jackson's standard behaviour, as illustrated by the following tests:

package com.example.demo;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

class MappingTests {

    @Test
    void jacksonMapping() throws JsonMappingException, JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Dummy dummy = objectMapper.readValue("{ \"tESt\": \"string\" }", Dummy.class);
        assertThat(dummy.getTESt()).isEqualTo("string");
    }

    @Test
    void jacksonLenientMapping() throws JsonMappingException, JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        Dummy dummy = objectMapper.readValue("{ \"tESt\": \"string\" }", Dummy.class);
        assertThat(dummy.getTESt()).isEqualTo("string");
    }

    static class Dummy { 

        private String tESt;

        public String getTESt() {
            return tESt;
        }

        public void setTESt(String tESt) {
            this.tESt = tESt;
        }

    }

}

The first will fail because there's an unknown property. The second will fail because, due to the unknown property, tESt is null. Boot disables FAIL_ON_UNKNOWN_PROPERTIES by default which is why you see null in your app.