杰克逊反序列化对象或数组

dar*_*rdo 14 java json jackson

我有一个杰克逊问题.

有没有办法反序列化可能有两种类型的属性,对于某些对象,它看起来像这样

"someObj" : { "obj1" : 5, etc....}
Run Code Online (Sandbox Code Playgroud)

然后对于其他人来说,它看起来像一个空数组,即

"someObj" : []
Run Code Online (Sandbox Code Playgroud)

任何帮助表示赞赏!

谢谢!

fab*_*ien 20

编辑:自Jackson 2.5.0以来,您可以使用DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_EMPTY_OBJECT来解决您的问题.

Bruce提供的解决方案有一些问题/缺点:

  • 您需要为每种类型复制该代码,以便以此方式反序列化
  • 应该重用ObjectMapper,因为它缓存序列化器和反序列化器,因此创建起来很昂贵.请参阅http://wiki.fasterxml.com/JacksonBestPracticesPerformance
  • 如果你的数组包含一些值,你可能希望让jackson无法反序列化它,因为它意味着编码时出现问题,你应该尽快看到并修复它.

这是我对该问题的"通用"解决方案:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

那么你仍然需要为每种不同的类型创建一个自定义反序列化器,但是这样写起来容易得多并且你不会复制任何逻辑:

public class Thing2Deserializer extends EmptyArrayAsNullDeserializer<Thing2> {

  public Thing2Deserializer() {
    super(Thing2.class);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你像往常一样使用它:

@JsonDeserialize(using = Thing2Deserializer.class)
Run Code Online (Sandbox Code Playgroud)

如果你找到了摆脱最后一步的方法,每种类型实现1个自定义反序列化器,我都是耳朵;)


Pro*_*uce 14

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

(您可以使用DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY来强制输入始终绑定到集合,但考虑到当前如何描述问题,这可能不是我采取的方法.)