I. Medina
I. Medina

Reputation: 125

Jackson: Is there a way to ignore 0/1 on Boolean deserialization?

I have a JSON object with a Boolean property that needs to allow only true or false during the deserialization.

Any value different of true and false should throw an exception.

How can I do that?


e.g.:

Valid json:

{
  "id":1,
  "isValid":true
}

Invalid json:

{
  "id":1,
  "isValid":1
}

Update

I tried what @Michał Ziober proposed and it worked fine.

As I'm implementing a Spring application (with Webflux) I just had to configure in a different way. I'm posting here what I did:

@Configuration
public class JacksonConfig extends DelegatingWebFluxConfiguration {

    @Override
    protected void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        configurer.defaultCodecs()
                .jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
    }
}

Upvotes: 5

Views: 3574

Answers (2)

Michał Ziober
Michał Ziober

Reputation: 38710

You need to disable ALLOW_COERCION_OF_SCALARS feature:

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);

        System.out.println(mapper.readValue(jsonFile, Pojo.class));
    }
}

class Pojo {

    private int id;
    private Boolean isValid;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boolean getIsValid() {
        return isValid;
    }

    public void setIsValid(Boolean valid) {
        isValid = valid;
    }

    @Override
    public String toString() {
        return "Pojo{" +
                "id=" + id +
                ", isValid=" + isValid +
                '}';
    }
}

Above code prints:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot coerce Number (1) for type java.lang.Boolean (enable MapperFeature.ALLOW_COERCION_OF_SCALARS to allow) at [Source: (File); line: 3, column: 14] (through reference chain: Pojo["isValid"]) at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)

Upvotes: 6

Yuriy Tsarkov
Yuriy Tsarkov

Reputation: 2568

Well, it's not a for-any-case implementation, but may be it'll be suitable for you:

public class TestBooleanDeserializer extends JsonDeserializer<Boolean> {

  @Override
  public Boolean deserialize(JsonParser jsonParser, DeserializationContext context)
      throws IOException, JsonProcessingException {
    String str = jsonParser.getText();    
    boolean val = Boolean.valueOf(str);
    if (!val && Objects.nonNull(str) && !Objects.equals("false", str.toLowerCase())) {
      throw new RuntimeException("invalid JSON");
    }
    return val;
  }

}

Since a Boolean.valueOf is a result of check of the java.lang.Boolean#

public static boolean parseBoolean(String s) {
    return ((s != null) && s.equalsIgnoreCase("true"));
}

here: boolean val = Boolean.valueOf(str); you'll check is it a true value or not. If not, you should check is it null or false: if (!val && Objects.nonNull(str) && !Objects.equals("false", str.toLowerCase())) if it's not you can throw an exception whatever you need. Otherwise you'll return a boolean value

Upvotes: 0

Related Questions