How do I deserialize to Boolean.class from Json object in case insensitive manner using Jackson?
You can add your custom com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
and implement handleWeirdStringValue
method in which you can check text and return Boolean.TRUE
or Boolean.FALSE
for other cases you want to handle:
import com.fasterxml.jackson.databind.DeserializationContext;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;import com.fasterxml.jackson.databind.json.JsonMapper;import java.io.IOException;public class JsonBooleanApp { public static void main(String[] args) throws Exception { ObjectMapper mapper = JsonMapper.builder() .addHandler(new DeserializationProblemHandler() { @Override public Object handleWeirdStringValue(DeserializationContext ctxt, Class<?> targetType, String valueToConvert, String failureMsg) throws IOException { if (targetType == Boolean.class) { return Boolean.TRUE.toString().equalsIgnoreCase(valueToConvert); } return super.handleWeirdStringValue(ctxt, targetType, valueToConvert, failureMsg); } }) .build(); System.out.println(mapper.readValue("{\"value\": \"True\"}", BooleanHolder.class)); System.out.println(mapper.readValue("{\"value\": \"true\"}", BooleanHolder.class)); System.out.println(mapper.readValue("{\"value\": \"TRUE\"}", BooleanHolder.class)); }}class BooleanHolder { private Boolean value; public Boolean getValue() { return value; } public void setValue(Boolean value) { this.value = value; } @Override public String toString() { return "BooleanHolder{" + "value=" + value + '}'; }}
Above code prints:
BooleanHolder{value=true}BooleanHolder{value=true}BooleanHolder{value=true}
Enable MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES
Default Boolean
deserialiser in version 2.10.0
does not check MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES
feature and is a final class which does not allow to override it easily. To make it aware about a feature we need to create a copy-paste
version with some changes. To make it as close as possible to original I created com.fasterxml.jackson.databind.deser.std
package and moved there below class:
package com.fasterxml.jackson.databind.deser.std;import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.core.JsonToken;import com.fasterxml.jackson.databind.DeserializationContext;import com.fasterxml.jackson.databind.MapperFeature;import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;import java.io.IOException;public final class BooleanDeserializerIgnoreCase extends NumberDeserializers.PrimitiveOrWrapperDeserializer<Boolean> { private static final long serialVersionUID = 1L; public final static BooleanDeserializerIgnoreCase primitiveInstance = new BooleanDeserializerIgnoreCase(Boolean.TYPE, Boolean.FALSE); public final static BooleanDeserializerIgnoreCase wrapperInstance = new BooleanDeserializerIgnoreCase(Boolean.class, null); public BooleanDeserializerIgnoreCase(Class<Boolean> cls, Boolean nvl) { super(cls, nvl, Boolean.FALSE); } @Override public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonToken t = p.getCurrentToken(); if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; } return _parseBoolean(p, ctxt); } // Since we can never have type info ("natural type"; String, Boolean, Integer, Double): // (is it an error to even call this version?) @Override public Boolean deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException { JsonToken t = p.getCurrentToken(); if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; } return _parseBoolean(p, ctxt); } protected final Boolean _parseBoolean(JsonParser p, DeserializationContext ctxt) throws IOException { JsonToken t = p.getCurrentToken(); if (t == JsonToken.VALUE_NULL) { return (Boolean) _coerceNullToken(ctxt, _primitive); } if (t == JsonToken.START_ARRAY) { // unwrapping? return _deserializeFromArray(p, ctxt); } // should accept ints too, (0 == false, otherwise true) if (t == JsonToken.VALUE_NUMBER_INT) { return Boolean.valueOf(_parseBooleanFromInt(p, ctxt)); } // And finally, let's allow Strings to be converted too if (t == JsonToken.VALUE_STRING) { return _deserializeFromString(p, ctxt); } // usually caller should have handled but: if (t == JsonToken.VALUE_TRUE) { return Boolean.TRUE; } if (t == JsonToken.VALUE_FALSE) { return Boolean.FALSE; } // Otherwise, no can do: return (Boolean) ctxt.handleUnexpectedToken(_valueClass, p); } protected final Boolean _deserializeFromString(JsonParser p, DeserializationContext ctxt) throws IOException { String text = p.getText().trim(); if (ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)) { if (Boolean.TRUE.toString().equalsIgnoreCase(text)) { return Boolean.TRUE; } if (Boolean.FALSE.toString().equalsIgnoreCase(text)) { return Boolean.FALSE; } } else { if ("true".equals(text) || "True".equals(text)) { _verifyStringForScalarCoercion(ctxt, text); return Boolean.TRUE; } if ("false".equals(text) || "False".equals(text)) { _verifyStringForScalarCoercion(ctxt, text); return Boolean.FALSE; } if (text.length() == 0) { return (Boolean) _coerceEmptyString(ctxt, _primitive); } if (_hasTextualNull(text)) { return (Boolean) _coerceTextualNull(ctxt, _primitive); } } return (Boolean) ctxt.handleWeirdStringValue(_valueClass, text, "only \"true\" or \"false\" recognized"); }}
Test case:
import com.fasterxml.jackson.databind.MapperFeature;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.deser.std.BooleanDeserializerIgnoreCase;import com.fasterxml.jackson.databind.json.JsonMapper;import com.fasterxml.jackson.databind.module.SimpleModule;public class JsonBooleanApp { public static void main(String[] args) throws Exception { SimpleModule booleanIgnoreCaseModule = new SimpleModule(); booleanIgnoreCaseModule.addDeserializer(Boolean.class, BooleanDeserializerIgnoreCase.wrapperInstance); booleanIgnoreCaseModule.addDeserializer(boolean.class, BooleanDeserializerIgnoreCase.primitiveInstance); ObjectMapper mapper = JsonMapper.builder() .addModule(booleanIgnoreCaseModule) .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES) .build(); System.out.println(mapper.readValue("{\"value\": \"True\"}", BooleanHolder.class)); System.out.println(mapper.readValue("{\"value\": \"true\"}", BooleanHolder.class)); System.out.println(mapper.readValue("{\"value\": \"TRUE\"}", BooleanHolder.class)); }}class BooleanHolder { private Boolean value; public Boolean getValue() { return value; } public void setValue(Boolean value) { this.value = value; } @Override public String toString() { return "BooleanHolder{" + "value=" + value + '}'; }}
Above code prints:
BooleanHolder{value=true}BooleanHolder{value=true}BooleanHolder{value=true}