多态数组序列化失败

时间:2015-02-06 15:42:00

标签: java json serialization jackson

我通过在基类上添加注释来启用Polymorphic序列化支持。我能够成功地对单个对象进行串行化,并且它将类型信息作为序列化数据的一部分进行编写。但是,如果我将对象存储在列表中并将其序列化,则不会发生同样的情况。

似乎这个问题已在1.6.3(http://jira.codehaus.org/browse/JACKSON-362

中修复

我正在使用Jackson 2.3.2并且仍然面临这个问题。

有人知道如何解决这个问题吗?

代码:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,property = "type")
@JsonSubTypes({@Type(value = Derived.class, name = "derived")})
public abstract class Base {

}

public class Derived extends Base {

    private String field;

    public String getField() {
        return field;
    }

    public void setField(String field) {
        this.field = field;
    }

}

public class Test {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new  ObjectMapper();
        Derived d = new Derived();
        d.setField("Name");
        Base b = d;
        System.out.println(mapper.writeValueAsString(b));
        List<Base> list = new ArrayList<Base>();
        list.add(d);
        System.out.println(mapper.writeValueAsString(list));
    }
}

输出:
{&#34;类型&#34;:&#34;衍生&#34;&#34;字段&#34;:&#34;名称&#34;}
[{&#34;字段&#34;:&#34;名称&#34;}]

谢谢,
普利文

3 个答案:

答案 0 :(得分:1)

答案是https://github.com/FasterXML/jackson-databind/issues/699

这是由于Java类型擦除:当序列化List时,所有Jackson看到的类型都是List(大致相当于List)。由于类型Object没有多态类型信息(注释),因此不会写入任何内容。 所以这不是杰克逊的错误,而是Java Type Erasure的一个不幸特征。 它不适用于数组,因为它们保留了元素类型信息(数组不是通用的;不同类型的数组是不同的类,而泛型类型主要是编译时语法糖)。

有三种主要方法可以解决这个问题:

pass full generic type using TypeReference (ObjectMapper has method like mapper.writerFor(new TypeReference<List<Base>>() { }).writeValue(....)
Sub-class List to something like public class BaseList extends ArrayList<Base>() { }, and pass that: this type WILL retain type information
Avoid using root-level List and Maps

我个人建议做(3),因为这样可以避免类型擦除的所有相关问题。 在我看来,JSON根值应始终是一个JSON对象,通常序列化为POJO。

然而,方法(2)将起作用,这是大多数用户所做的。它确实需要使用额外的助手类。

方法(1)可能有效也可能无效;问题是强制类型信息也会影响实际值序列化。因此,虽然它会添加类型ID,但可能会导致某些属性未被序列化。

答案 1 :(得分:1)

这个问题可以通过使用数组而不是列表来解决(因为列表确实键入了擦除):

例如,您的上述测试用例可以写成:

public class Test {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new  ObjectMapper();
        Derived d = new Derived();
        d.setField("Name");
        Base b = d;
        System.out.println(mapper.writeValueAsString(b));
        List<Base> list = new ArrayList<Base>();
        list.add(d);
        System.out.println(mapper.writeValueAsString(
                      list.toArray(new Base[list.size]) // <--This Part
            ));
    }
}

答案 2 :(得分:0)

我对象数组有同样的问题。对象[]不携带类型信息,但单个对象可以。令人遗憾的是杰克逊没有自动处理这个问题。

两种可能的解决方案: 1.类型化数组序列化工作正常:

[HttpGet]
[Route("students")]
[SwaggerOperation(Tags = new[] { "Student" })]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(ResponseModel<IList<Student>>))]
[SwaggerResponseExample(HttpStatusCode.OK, typeof(StudentResponseExample))]
[SwaggerResponse(HttpStatusCode.InternalServerError)]
public IHttpActionResult SearchStudent(long? SyncDate = null,int? OffSet = null,int? Limit = null)
    {
        // Use the variables now here
        .
        .
        .

    }

这实际上会产生预期的结果,因为Base []具有类型信息。

  1. 我解决了自定义序列化程序的问题。
  2. Serializer:

    Base[] myArray = Base[]{d};
    mapper.writeValueAsString(myArray)
    

    ObjectMapper配置:

    public class ObjectArraySerializer extends StdSerializer<Object[]> {
      public ObjectArraySerializer(final Class<Object[]> vc) {
        super(vc);
      }
    
      @Override
      public void serialize(
          final Object[] data,
          final JsonGenerator gen,
          final SerializerProvider provider) throws IOException {
        gen.writeStartArray();
        for (Object obj : data) {
          gen.writeObject(obj);
        }
        gen.writeEndArray();
      }
    }