如何使用杰克逊以不区分大小写的方式从Json对象反序列化为Boolean.class? [英] How do I deserialize to Boolean.class from Json object in case insensitive manner using Jackson?
问题描述
我正在使用Jackson 2.10.x反序列化格式的Json
{ myKey: "true"}
.可能的变化形式是{ myKey: "True"}
,{ myKey: "TRUE"}
,对于false也是类似的.
我需要反序列化的POJO具有属性myKey :: Boolean.class.
I am using Jackson 2.10.x to deserialise a Json of the format
{ myKey: "true"}
. The possible variations are { myKey: "True"}
, { myKey: "TRUE"}
and similarly for false.
The POJO that I need to deserialize to has attribute myKey::Boolean.class.
我不拥有POJO源,因此无法在特定属性上设置Json属性.
I do not own the POJO source and hence cannot set Json property on the specific attribute.
当值分别为"true"和"True"时,Jackson可以反序列化,但如果值为"TRUE",则不能反序列化. 我尝试按以下方式使用MapperFeature ACCEPT_CASE_INSENSITIVE_VALUES,但这无济于事
Jackson is able to deserialize when the value is "true" and "True" but not when it is "TRUE". I tried using the MapperFeature ACCEPT_CASE_INSENSITIVE_VALUES as follows but that did not help
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)
异常消息是
Cannot deserialize value of type `java.lang.Boolean` from String "TRUE": only "true" or "false" recognized at [Source: UNKNOWN; line: -1, column: -1]
推荐答案
您可以添加自定义com.fasterxml.jackson.databind.deser.DeserializationProblemHandler
并实现handleWeirdStringValue
方法,在该方法中,您可以检查文本并针对其他情况返回Boolean.TRUE
或Boolean.FALSE
要处理:
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 +
'}';
}
}
上面的代码显示:
BooleanHolder{value=true}
BooleanHolder{value=true}
BooleanHolder{value=true}
启用MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES
版本2.10.0
中的默认Boolean
解串器不检查MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES
功能,它是最终类,不允许轻易覆盖它.为了使其了解某个功能,我们需要创建一个具有一些更改的copy-paste
版本.为了使其尽可能接近原始版本,我创建了com.fasterxml.jackson.databind.deser.std
程序包,并将其移至类下面的位置:
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");
}
}
测试用例:
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 +
'}';
}
}
上面的代码显示:
BooleanHolder{value=true}
BooleanHolder{value=true}
BooleanHolder{value=true}
这篇关于如何使用杰克逊以不区分大小写的方式从Json对象反序列化为Boolean.class?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!