简体   繁体   English

How do I deserialize to Boolean.class from Json object in case insensitive manner using Jackson?

[英]How do I deserialize to Boolean.class from Json object in case insensitive manner using Jackson?

I am using Jackson 2.10.x to deserialise a Json of the format { myKey: "true"} .我正在使用 Jackson 2.10.x 反序列化格式为{ myKey: "true"}的 Json 。 The possible variations are { myKey: "True"} , { myKey: "TRUE"} and similarly for false.可能的变化是{ myKey: "True"} , { myKey: "TRUE"}和类似的 false。 The POJO that I need to deserialize to has attribute myKey::Boolean.class.我需要反序列化到的 POJO 具有属性 myKey::Boolean.class。

I do not own the POJO source and hence cannot set Json property on the specific attribute.我不拥有 POJO 源,因此无法在特定属性上设置 Json 属性。

Jackson is able to deserialize when the value is "true" and "True" but not when it is "TRUE". Jackson 能够在值为“true”和“True”时反序列化,但不能在值为“TRUE”时反序列化。 I tried using the MapperFeature ACCEPT_CASE_INSENSITIVE_VALUES as follows but that did not help我尝试使用 MapperFeature ACCEPT_CASE_INSENSITIVE_VALUES 如下,但这没有帮助

objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES)

The exception message is异常消息是

Cannot deserialize value of type `java.lang.Boolean` from String "TRUE": only "true" or "false" recognized at [Source: UNKNOWN; line: -1, column: -1] 

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: 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启用 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.版本2.10.0中的默认Boolean解串器不检查MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES功能,并且是最终的 class 不允许轻易覆盖它。 To make it aware about a feature we need to create a copy-paste version with some changes.为了让它知道某个功能,我们需要创建一个带有一些更改的copy-paste版本。 To make it as close as possible to original I created com.fasterxml.jackson.databind.deser.std package and moved there below class: 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}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM