M. Akhil
M. Akhil

Reputation: 113

Restrict string data type to only string types for request body in Spring boot 2.4 (Jackson)

I have created my request POJO as follows

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Notification {

    @NotNull
    private String clientId;
    private String userId;  
    @NotNull
    private String requestingService;
    @NotNull
    private String message;
    @NotNull
    private String messageType;

when I send request body as follow, it is working fine.

{
   "clientId":"9563",
    "userId":"5855541",
    "requestingService":"cm-dm-service",
    "message":"Document Created",
    "messageType":"user-msg"
}

But when I sent like below

{
   "clientId":"9563",
    "userId":true,
    "requestingService":"cm-dm-service",
    "message":"Document Created",
    "messageType":"user-msg"
}

Here is my controller

public ResponseEntity<Status> createNotification(@RequestBody @Valid Notification notification,
            BindingResult bindingResult, HttpServletRequest request) throws AppException {

Expected: throw some error

Actual: converting true value for userId to string by jackson.

please let me know is there a way to acheive the Expected behaviour

Upvotes: 3

Views: 1884

Answers (2)

srini
srini

Reputation: 71

The jackson NumberDeserializers.BooleanDeserializer is programmed to convert boolean to String.

We can override the deserializer with ours and prevent the conversion and throw an exception instead.

I can give you an example, you try to implement it to your problem statement.

  1. Create a boolean deserialize class

    public class MyDeser extends JsonDeserializer {
        @Override
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            JsonToken t = p.getCurrentToken();
            if (t.isBoolean()) {
                throw new Exception();
            }
            else if (t.isNumeric()) {
                throw new Exception();
            }
            else if (t == JsonToken.VALUE_STRING) {
                return p.getValueAsString();
            }
            return null;
        }
    }

  1. Now inject the deserializer to our application

    @SpringBootApplication
     @Configuration
     public class Application {
         @Bean
         public SimpleModule injectDeser() {
             return new SimpleModule().addDeserializer(String.class, new MyDeser());
         }
         public static void main(String[] args) {
             SpringApplication.run(Application.class, args);
         }
     }

Upvotes: 2

Michał Ziober
Michał Ziober

Reputation: 38635

By default, com.fasterxml.jackson.databind.deser.std.StringDeserializer accepts scalar values. You can implement custom deserialiser and throw exception in this case:

class StrictStringDeserializer extends StringDeserializer {
    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        JsonToken token = p.currentToken();
        if (token.isScalarValue()) {
            ctxt.reportInputMismatch(String.class, "%s is not a `String` value!", token.toString());
            return null;
        }
        return super.deserialize(p, ctxt);
    }
}

To register it use SimpleModule:

SimpleModule strictModule = new SimpleModule();
strictModule.addDeserializer(String.class, new StrictStringDeserializer());

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(strictModule);

To how to do that in Spring read: Customize the Jackson ObjectMapper.

If you only want to change it for a one field: userId use JsonDeserialize annotation:

@JsonDeserialize(using = StrictStringDeserializer.class)
private String userId;

See also:

Upvotes: 2

Related Questions