如何使用杰克逊以不区分大小写的方式从Json对象反序列化为Boolean.class? [英] How do I deserialize to Boolean.class from Json object in case insensitive manner using Jackson?

查看:130
本文介绍了如何使用杰克逊以不区分大小写的方式从Json对象反序列化为Boolean.class?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用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.TRUEBoolean.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆