[英]Jackson deserialize object or array
我有一个杰克逊问题。
有没有办法反序列化可能有两种类型的属性,对于某些对象,它看起来像这样
"someObj" : { "obj1" : 5, etc....}
那么对于其他人来说,它显示为一个空数组,即
"someObj" : []
任何帮助表示赞赏!
谢谢!
编辑:从 Jackson 2.5.0 开始,您可以使用 DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_EMPTY_OBJECT 来解决您的问题。
Bruce 提供的解决方案有一些问题/缺点:
这是我针对该问题的“通用”解决方案:
public abstract class EmptyArrayAsNullDeserializer<T> extends JsonDeserializer<T> {
private final Class<T> clazz;
protected EmptyArrayAsNullDeserializer(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
if (node.isArray() && !node.getElements().hasNext()) {
return null;
}
return oc.treeToValue(node, clazz);
}
}
那么你仍然需要为每种不同的类型创建一个自定义的反序列化器,但这更容易编写,而且你不需要复制任何逻辑:
public class Thing2Deserializer extends EmptyArrayAsNullDeserializer<Thing2> {
public Thing2Deserializer() {
super(Thing2.class);
}
}
然后你像往常一样使用它:
@JsonDeserialize(using = Thing2Deserializer.class)
如果您找到一种方法来摆脱最后一步,即为每种类型实现 1 个自定义解串器,我会全力以赴;)
Jackson 目前没有内置配置来自动处理这种特殊情况,因此需要自定义反序列化处理。
以下是此类自定义反序列化的示例。
import java.io.IOException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.Version;
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.module.SimpleModule;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
// {"property1":{"property2":42}}
String json1 = "{\"property1\":{\"property2\":42}}";
// {"property1":[]}
String json2 = "{\"property1\":[]}";
SimpleModule module = new SimpleModule("", Version.unknownVersion());
module.addDeserializer(Thing2.class, new ArrayAsNullDeserializer());
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY).withModule(module);
Thing1 firstThing = mapper.readValue(json1, Thing1.class);
System.out.println(firstThing);
// output:
// Thing1: property1=Thing2: property2=42
Thing1 secondThing = mapper.readValue(json2, Thing1.class);
System.out.println(secondThing);
// output:
// Thing1: property1=null
}
}
class Thing1
{
Thing2 property1;
@Override
public String toString()
{
return String.format("Thing1: property1=%s", property1);
}
}
class Thing2
{
int property2;
@Override
public String toString()
{
return String.format("Thing2: property2=%d", property2);
}
}
class ArrayAsNullDeserializer extends JsonDeserializer<Thing2>
{
@Override
public Thing2 deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException
{
JsonNode node = jp.readValueAsTree();
if (node.isObject())
return new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY).readValue(node, Thing2.class);
return null;
}
}
(您可以使用 DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY 强制输入始终绑定到集合,但鉴于当前描述问题的方式,这可能不是我采用的方法。)
对于将使用 BeanDeserializer 反序列化的对象,还有另一个角度更一般地解决这个问题,方法是创建一个BeanDeserializerModifier
并将其注册到您的映射器。 BeanDeserializerModifier
是子类化BeanDeserializerFactory
的一种替代方法,它使您有机会返回将使用的普通反序列化器以外的其他内容,或对其进行修改。
因此,首先创建一个新的JsonDeserializer
,它可以在构建时接受另一个反序列化器,然后保留该序列化器。 在反序列化方法,您可以检查,如果你被通过了JsonParser
,因此目前指着一个JsonToken.START_ARRAY
。 如果您没有通过JsonToken.START_ARRAY
,那么只需使用在创建时传入此自定义反序列化的默认反序列化器。
最后,确保实现ResolvableDeserializer
,以便默认解串器正确附加到您的自定义解串器正在使用的上下文。
class ArrayAsNullDeserialzer extends JsonDeserializer implements ResolvableDeserializer {
JsonDeserializer<?> mDefaultDeserializer;
@Override
/* Make sure the wrapped deserializer is usable in this deserializer's contexts */
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
((ResolvableDeserializer) mDefaultDeserializer).resolve(ctxt);
}
/* Pass in the deserializer given to you by BeanDeserializerModifier */
public ArrayAsNullDeserialzer(JsonDeserializer<?> defaultDeserializer) {
mDefaultDeserializer = defaultDeserializer;
}
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken firstToken = jp.getCurrentToken();
if (firstToken == JsonToken.START_ARRAY) {
//Optionally, fail if this is something besides an empty array
return null;
} else {
return mDefaultDeserializer.deserialize(jp, ctxt);
}
}
}
现在我们有了通用的反序列化器钩子,让我们创建一个可以使用它的修饰符。 这很简单,只需在modifyDeserializer
中实现modifyDeserializer
方法即可。 您将通过将已用于反序列化豆解串器。 它还向您传递将被反序列化的 BeanDesc,因此您可以在此处控制是否要将所有类型的 [] 处理为 null。
public class ArrayAsNullDeserialzerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if ( true /* or check beanDesc to only do this for certain types, for example */ ) {
return new ArrayAsNullDeserializer(deserializer);
} else {
return deserializer;
}
}
}
最后,您需要使用 ObjectMapper 注册 BeanDeserializerModifier。 要做到这一点,创建一个模块,并在设置中添加修饰符(不幸的是,SimpleModules 似乎没有这个钩子)。 你可以在别处阅读更多关于模块的信息,但如果你还没有要添加的模块,这里有一个例子:
Module m = new Module() {
@Override public String getModuleName() { return "MyMapperModule"; }
@Override public Version version() { return Version.unknownVersion(); }
@Override public void setupModule(Module.SetupContext context) {
context.addBeanDeserializerModifier(new ArrayAsNullDeserialzerModifier());
}
};
其他答案都不适合我:
ObjectMapper
,我们使用@JsonDeserialize
注释来安装反序列化器。ObjectMapper
。Map
,这似乎不适用于ObjectCodec.treeToValue
。这是最终有效的解决方案:
public class EmptyArrayToEmptyMapDeserializer extends JsonDeserializer<Map<String, SomeComplexType>> {
@Override
public Map<String, SomeComplexType> deserialize(JsonParser parser,
DeserializationContext context) throws IOException {
if (parser.getCurrentToken() == JsonToken.START_ARRAY) {
// Not sure what the parser does with the following END_ARRAY token, probably ignores it somehow.
return Map.of();
}
return context.readValue(parser, TypeFactory.defaultInstance().constructMapType(Map.class, String.class, SomeComplexType.class));
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.