3

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

M. Akhil
  • 113
  • 1
  • 11

2 Answers2

2

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);
         }
     }

srini
  • 71
  • 5
  • Here t.asString() returned null. So I used p.getValueAsString(); and also changed return type to String. public String deserialize(.....). After doing this, code is working fine as expected. Thank you Srinivasa Raghavan. – M. Akhil Dec 28 '20 at 09:36
  • Hi, I have edited it as you said. If this is the solution you asked for, please accept this answer as the solution, so that it will be useful for others also. Thanks – srini Dec 28 '20 at 15:19
2

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:

Michał Ziober
  • 37,175
  • 18
  • 99
  • 146