简体   繁体   English

杰克逊的“轻松”字段名称

[英]“Relaxed” fields names for Jackson

I'm working on Jackson configuration and I wonder if there is any option to deserialise different kinds of field patterns. 我正在Jackson配置上工作,我想知道是否有任何选项可以反序列化不同种类的场模式。

For example, I have an object: 例如,我有一个对象:

class DeserializeIt {
    String fieldOne;
    String fieldOneAndHalf;
    String fieldTwo;
    String fieldThree;
    String fieldFour;

   //getters setters etc.
}

And I have below JSON payload: 而且我有下面的JSON有效负载:

{
    "fieldOne" : "value1",
    "field_ONE-and_Half": "value15",
    "FIELD_TWO": "value2",
    "FIELD_THREE" : "value3",
    "field_four": "value4"
}

I would like to deserialize all these field names to camel case without an exception. 我想将所有这些字段名称反序列化为驼峰式,无一例外。

I tried to create my custom PropertyNamingStrategy but it goes from another direction: it does not convert delimitered fields to camel case, it tries to convert the objects fields and search for them in the parsed string. 我试图创建我的自定义PropertyNamingStrategy但是它从另一个方向出发:它不会将定界字段转换为驼峰式大小写,而是尝试转换对象字段并在已解析的字符串中搜索它们。

And since I cannot pass a list of possible strings instead of one variation ( fieldOne can become field-one , field_one , field-ONE etc.), this does not work. 而且由于我无法传递可能的字符串列表而不是一个变体( fieldOne可以变为field-onefield_onefield-ONE等),所以这不起作用。

Do you know what else could I configure for such a relaxed deserialization? 您知道为这种轻松的反序列化还能配置什么吗?

We need to extend com.fasterxml.jackson.databind.deser.BeanDeserializerModifier and com.fasterxml.jackson.databind.deser.BeanDeserializer which deserialises POJO classes. 我们需要扩展com.fasterxml.jackson.databind.deser.BeanDeserializerModifiercom.fasterxml.jackson.databind.deser.BeanDeserializer来反序列化POJO类。 Below solution depends from version you are using because I copied some code from base class which is not ready for intercepting extra functionality. 下面的解决方案取决于您使用的version ,因为我从基类中复制了一些代码,这些代码尚无法拦截额外的功能。 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. 如果您的POJO类没有任何额外的配置,则会调用vanillaDeserialize方法,我们将尝试vanillaDeserialize方法进行改进。

In other case you need to debug this deserialiser and updated other places if needed. 在其他情况下,您需要调试此解串器并在需要时更新其他位置。 Below solution uses version 2.9.8 . 下面的解决方案使用版本2.9.8

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.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.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 relaxedModule = new SimpleModule();
        relaxedModule.setDeserializerModifier(new RelaxedBeanDeserializerModifier());

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

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

class RelaxedBeanDeserializerModifier 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 RelaxedBeanDeserializer((BeanDeserializer) base);
        }

        return base;
    }
}

class RelaxedBeanDeserializer extends BeanDeserializer {

    private Map<String, String> properties = new HashMap<>();

    public RelaxedBeanDeserializer(BeanDeserializerBase src) {
        super(src);
        _beanProperties.forEach(property -> {
            properties.put(property.getName().toLowerCase(), property.getName());
        });
    }

    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);

        if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
            String propName = p.getCurrentName();

            do {
                String relaxedName = getRelaxedName(propName);
                String mappedName = properties.get(relaxedName);
                defaultImplementation(p, ctxt, bean, mappedName);
            } 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);
    }

    private String getRelaxedName(String name) {
        return name.replaceAll("[_\\-]", "").toLowerCase();
    }
}

Above code prints: 上面的代码打印:

DeserializeIt{fieldOne='value1', fieldOneAndHalf='value15', fieldTwo='value2', fieldThree='value3', fieldFour='value4'}

See also: 也可以看看:

From Jackson 2.9, you can provide multiple possible properties names for deserialization using the @JsonAlias annotation . 在Jackson 2.9中,可以使用@JsonAlias批注提供多个可能的属性名称以进行反序列化。 On your example, it would be like this: 在您的示例中,它将是这样的:

class DeserializeIt {
  @JsonAlias("fieldOne") 
  String fieldOne;

  @JsonAlias("field_ONE-and_Half") 
  String fieldOneAndHalf;

  @JsonAlias("FIELD_TWO") 
  String fieldTwo;

  @JsonAlias("FIELD_THREE") 
  String fieldThree;
  // and so on...
}

对自己有用的方法:我添加了一个AOP组件,该组件将传入对象的所有字段重命名为Camel案例。

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

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