Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable conversion of scalars to strings when deserializing with Jackson

I want to identify numerical values inserted without quotation marks (as strings) in JSON sent through the request body of a POST request:

For example, this would be the wrong JSON format as the age field does not contain quotation marks:

{
  "Student":{
    "Name": "John",
    "Age":  12
  }
}

The correct JSON format would be:

{
  "Student":{ 
    "Name": "John",
    "Age":  "12"
  }
}

In my code, I've defined the datatype of the age field as a String, hence "12" should be the correct input. However, no error message is thrown, even when 12 is used.

It seems Jackson automatically converts the numerical values into strings. How can I identify numerical values and return a message?

This is what I tried so far to identify these numerical values:

public List<Student> getMultiple(StudentDTO Student) {
    if(Student.getAge().getClass()==String.class) {
        System.out.println("Age entered correctly as String");
    } else{
        System.out.println("Please insert age value inside inverted commas");
    }
}

However, this is not printing "Please insert age value inside inverted commas" to the console when the age is inserted without quotation marks.

like image 773
Kasun Karunarathna Avatar asked Apr 24 '19 14:04

Kasun Karunarathna


1 Answers

If you're using Spring boot, by default it uses Jackson to parse JSON. There's no configuration option within Jackson to disable this feature, as mentioned within this issue. The solution is to register a custom JsonDeserializer that throws an exception as soon as it encounters any other token than JsonToken.VALUE_STRING

public class StringOnlyDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        if (!JsonToken.VALUE_STRING.equals(jsonParser.getCurrentToken())) {
            throw deserializationContext.wrongTokenException(jsonParser, String.class, JsonToken.VALUE_STRING, "No type conversion is allowed, string expected");
        } else {
            return jsonParser.getValueAsString();
        }
    }
}

If you only want to apply this to certain classes or fields, you can annotate those with the @JsonDeserialize annotation. For example:

public class Student {
    private String name;
    @JsonDeserialize(using = StringOnlyDeserializer.class)
    private String age;

    // TODO: Getters + Setters
}

Alternatively, you can register a custom Jackson module by registering a SimpleModule bean that automatically deserializes all strings using the StringOnlyDeserializer. For example:

@Bean
public Module customModule() {
    SimpleModule customModule = new SimpleModule();
    customModule.addDeserializer(String.class, new StringOnlyDeserializer());
    return customModule;
}

This is similar to what Eugen suggested.

If you run your application now, and you pass an invalid age, such as 12, 12.3 or [12]it will throw an exception with a message like:

JSON parse error: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string\n at [Source: (PushbackInputStream); line: 3, column: 9] (through reference chain: com.example.xyz.Student[\"age\"])
like image 103
g00glen00b Avatar answered Sep 27 '22 21:09

g00glen00b