Jackson可以不区分大小写的方式检查重复属性吗? [英] Can Jackson check for duplicated properties in a case insensitive way?

查看:166
本文介绍了Jackson可以不区分大小写的方式检查重复属性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Jackson JSON将一些JSON对象转换为POJO类.这种反序列化应该不区分大小写,并且不允许具有不区分大小写重复名称的属性.

I am using Jackson JSON to convert some JSON objects to POJO classes. This deserialization should be case insensitive, and it should not allow attributes with case insensitive duplicate names.

按如下所示配置ObjectMapper可以实现不区分大小写的反序列化,并且可以对严格具有相同名称的属性进行故障处理:

Configuring the ObjectMapper as shown below enables case insensitive deserialization and failing upon attributes which have strictly the same name:

final ObjectMapper objectMapper;
objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
objectMapper.enable(JsonParser.Feature.STRICT_DUPLICATE_DETECTION);

但是,当输入包含两个具有相同名称和大小写的属性时,它不会失败,例如:

However it does not fail when the input contains two attributes with same names and different case, such as the following:

{
   "Name": "name01",
   "NAME": "name02"
}

有什么方法可以配置ObjectMapper在这种情况下失败?

Is there any way to configureObjectMapper to fail under such circumstances?

推荐答案

来自STRICT_DUPLICATE_DETECTION文档:

确定JsonParser是否将显式检查以下内容的功能 不会遇到重复的JSON对象字段名称.如果启用, 解析器将检查上下文中的所有名称,并通过以下方式报告重复项 抛出JsonParseException;如果禁用,解析器将不会这样做 检查. 后一种情况的假设是呼叫者会照顾 在更高级别上处理重复项:例如,数据绑定具有 用于指定要在此处进行检测的功能.请注意,启用 由于必须存储,此功能将导致性能开销 并查看其他信息:这通常会增加20%到30% 基本解析的执行时间.

Feature that determines whether JsonParser will explicitly check that no duplicate JSON Object field names are encountered. If enabled, parser will check all names within context and report duplicates by throwing a JsonParseException; if disabled, parser will not do such checking. Assumption in latter case is that caller takes care of handling duplicates at a higher level: data-binding, for example, has features to specify detection to be done there. Note that enabling this feature will incur performance overhead due to having to store and check additional information: this typically adds 20-30% to execution time for basic parsing.

JSON默认情况下区分大小写,而Jackson默认情况下未启用使其不敏感的主要原因之一.但是我们可以扩展基本实现并添加验证.我们需要扩展对POJO类进行反序列化的com.fasterxml.jackson.databind.deser.BeanDeserializerModifiercom.fasterxml.jackson.databind.deser.BeanDeserializer.下面的解决方案取决于您使用的版本,因为我从基类中复制了一些代码,这些代码尚无法拦截额外的功能.如果您的POJO类没有任何额外的配置,则将调用vanillaDeserialize方法,我们将尝试改进这一方法.让我们实现它:

JSON by default is case sensitive and this one of main reason why making it insensitive is not enabled by default in Jackson. But we can extend basic implementation and add validation. We need to extend com.fasterxml.jackson.databind.deser.BeanDeserializerModifier and com.fasterxml.jackson.databind.deser.BeanDeserializer which deserialises POJO classes. Below solution depends from version you are using because I copied some code from base class which is not ready for intercepting extra functionality. If you do not have any extra configuration for your POJO classes vanillaDeserialize method will be invoked and this one we will try to improve. Let's implement it:

class InsensitiveBeanDeserializerModifier extends BeanDeserializerModifier {

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        JsonDeserializer<?> base = super.modifyDeserializer(config, beanDesc, deserializer);
        if (base instanceof BeanDeserializer) {
            return new InsensitiveBeanDeserializer((BeanDeserializer) base);
        }

        return base;
    }
}

class InsensitiveBeanDeserializer extends BeanDeserializer {

    public InsensitiveBeanDeserializer(BeanDeserializerBase src) {
        super(src);
    }

    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        // common case first
        if (p.isExpectedStartObjectToken()) {
            if (_vanillaProcessing) {
                return vanillaDeserialize(p, ctxt, p.nextToken());
            }
            // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
            //    what it is, including "expected behavior".
            p.nextToken();
            if (_objectIdReader != null) {
                return deserializeWithObjectId(p, ctxt);
            }
            return deserializeFromObject(p, ctxt);
        }
        return _deserializeOther(p, ctxt, p.getCurrentToken());
    }

    protected Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException {
        final Object bean = _valueInstantiator.createUsingDefault(ctxt);
        // [databind#631]: Assign current value, to be accessible by custom serializers
        p.setCurrentValue(bean);
        Map<String, String> names = new HashMap<>();
        if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
            String propName = p.getCurrentName();
            do {
                String oldName = names.put(propName.toLowerCase(), propName);
                if (oldName != null) {
                    String msg = "Properties '" + propName + "' and '" + oldName + "' are the same!";
                    throw new DuplicateInsensitiveKeysException(p, msg);
                }

                defaultImplementation(p, ctxt, bean, propName);
            } while ((propName = p.nextFieldName()) != null);
        }
        return bean;
    }

    private void defaultImplementation(JsonParser p, DeserializationContext ctxt, Object bean, String propName) throws IOException {
        p.nextToken();
        SettableBeanProperty prop = _beanProperties.find(propName);

        if (prop != null) { // normal case
            try {
                prop.deserializeAndSet(p, ctxt, bean);
            } catch (Exception e) {
                wrapAndThrow(e, bean, propName, ctxt);
            }
            return;
        }
        handleUnknownVanilla(p, ctxt, bean, propName);
    }

    public static class DuplicateInsensitiveKeysException extends JsonMappingException {

        public DuplicateInsensitiveKeysException(Closeable processor, String msg) {
            super(processor, msg);
        }
    }
}

示例用法:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class JsonApp {

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

        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new InsensitiveBeanDeserializerModifier());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);

        mapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES);

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

对于上述JSON有效负载打印:

For above JSON payloads prints:

Exception in thread "main" InsensitiveBeanDeserializer$DuplicateInsensitiveKeysException: Properties 'NAME' and 'Name' are the same!

这篇关于Jackson可以不区分大小写的方式检查重复属性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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