jackson反序列化对象或数组
我有jackson的问题。
有没有办法反序列化可能有两种类型的属性,对于某些对象,它看起来像这样
"someObj" : { "obj1" : 5, etc....}
那么对于其他人来说,它看起来像一个空数组,即
"someObj" : []
任何帮助表示赞赏!
谢谢!
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 { @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来强制输入始终绑定到集合,但考虑到当前如何描述问题,这可能不是我采取的方法。)
编辑:自Jackson 2.5.0以来,您可以使用DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_EMPTY_OBJECT来解决您的问题。
Bruce提供的解决方案有一些问题/缺点:
- 您需要为每种类型复制该代码,以便以这种方式反序列化
- 应该重用ObjectMapper,因为它缓存序列化器和反序列化器,因此创建起来很昂贵。 请参阅http://wiki.fasterxml.com/JacksonBestPracticesPerformance
- 如果你的数组包含一些值,你可能希望让jackson无法反序列化它,因为它意味着在编码时出现了问题,你应该尽快看到并修复它。
这是我对该问题的“通用”解决方案:
public abstract class EmptyArrayAsNullDeserializer extends JsonDeserializer { private final Class clazz; protected EmptyArrayAsNullDeserializer(Class 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 { public Thing2Deserializer() { super(Thing2.class); } }
然后像往常一样使用它:
@JsonDeserialize(using = Thing2Deserializer.class)
如果你找到了摆脱最后一步的方法, 即每种类型实现1个自定义反序列化器,我都是耳朵;)
通过创建BeanDeserializerModifier
并将其注册到映射器,可以使用BeanDeserializer对通过反序列化的对象更一般地解决此问题。 BeanDeserializerModifier
是BeanDeserializerModifier
的一种替代方法,它使您有机会返回除正常反序列化器之外的其他内容,或者修改它。
因此,首先创建一个新的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
方法即可。 您将传递用于反序列化bean的反序列化程序。 它还会传递将被反序列化的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()); } };