我正在研究Jackson
的配置,我想知道是否有任何选项可以反序列化不同种类的字段模式。
例如,我有一个对象:
class DeserializeIt {
String fieldOne;
String fieldOneAndHalf;
String fieldTwo;
String fieldThree;
String fieldFour;
//getters setters etc.
}
我的有效载荷低于JSON
:
{
"fieldOne" : "value1",
"field_ONE-and_Half": "value15",
"FIELD_TWO": "value2",
"FIELD_THREE" : "value3",
"field_four": "value4"
}
我想将所有这些字段名称反序列化为驼峰式大小写。
我试图创建自定义PropertyNamingStrategy
,但是它从另一个方向出发:它不会将定界字段转换为驼峰式大小写,而是尝试转换对象字段并在已解析的字符串中搜索它们。
并且由于我无法传递可能的字符串列表而不是一个变体(fieldOne
可以变成field-one
,field_one
,field-ONE
等),所以这不起作用
您知道我还可以为这种轻松的反序列化配置什么吗?
答案 0 :(得分:0)
我们需要扩展com.fasterxml.jackson.databind.deser.BeanDeserializerModifier
和com.fasterxml.jackson.databind.deser.BeanDeserializer
来反序列化POJO
类。下面的解决方案取决于您使用的version
,因为我从基类中复制了一些代码,这些代码尚无法拦截额外的功能。如果您的POJO
类vanillaDeserialize
方法没有任何额外的配置,我们将尝试改进这一方法。
在其他情况下,您需要调试此解串器并在需要时更新其他位置。以下解决方案使用版本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();
}
}
上面的代码显示:
DeserializeIt{fieldOne='value1', fieldOneAndHalf='value15', fieldTwo='value2', fieldThree='value3', fieldFour='value4'}
另请参阅:
答案 1 :(得分:0)
从Jackson 2.9开始,您可以使用 @JsonAlias annotation。在您的示例中,就像这样:
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...
}
答案 2 :(得分:0)
对自己有用的方法:我添加了一个AOP组件,将传入对象的所有字段重命名为Camel外壳。