当前位置: 首页 > 知识库问答 >
问题:

为什么Jackson多态序列化不能在列表中工作?

傅奕
2023-03-14

杰克逊做了一件非常奇怪的事,我找不到任何解释。我正在进行多态序列化,当一个对象独立运行时,它可以完美地工作。但是,如果将同一对象放入列表并序列化该列表,则会删除类型信息。

它丢失类型信息的事实会导致可疑的类型擦除。但是这发生在序列化列表内容的过程中;Jackson所要做的就是检查它正在序列化的当前对象以确定其类型。

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.List;

public class Test {

  @JsonIgnoreProperties(ignoreUnknown = true)
  @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
  @JsonSubTypes({
    @Type(value = Dog.class, name = "dog"),
    @Type(value = Cat.class, name = "cat")})
  public interface Animal {}

  @JsonTypeName("dog")
  public static class Dog implements Animal {
    private String name;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

  @JsonTypeName("cat")
  public static class Cat implements Animal {
    private String name;

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }
  }

  public static void main(String[] args) throws JsonProcessingException {
    List<Cat> list = new ArrayList<>();
    list.add(new Cat());
    System.out.println(new ObjectMapper().writeValueAsString(list));
    System.out.println(new ObjectMapper().writeValueAsString(list.get(0)));
  }
}

以下是输出:

[{"name":null}]
{"@type":"cat","name":null}

如您所见,当对象位于列表中时,Jackson没有添加类型信息。有人知道为什么会这样吗?

共有3个答案

颜欣怡
2023-03-14

我也遇到了这个问题,这是我更喜欢的解决方法(我使用Kotlin,但Java基本相同)

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY)
@JsonSubTypes(
        JsonSubTypes.Type(value = Bob::class, name = "bob"),
        JsonSubTypes.Type(value = Alice::class, name = "alice")
)
abstract class Person {

    abstract val jacksonMarker: String
        @JsonProperty("@type")
        get

    // ... rest of the class
}

子类:

class Bob: Person {

    override val jacksonMarker: String
        get() = "bob"

    // ... rest of the class

}


class Alice: Person {

    override val jacksonMarker: String
        get() = "alice"

    // ... rest of the class

}

你准备好了。

单于楚
2023-03-14

Sotirios Delimanolis给出的答案是正确的。然而,我认为最好将此解决方案作为一个单独的答案发布。如果您所处的环境无法为需要返回的每种类型(如Jersey/SpringMVC webapp)更改ObjectMapper,那么还有另一种选择。

您可以简单地在包含类型的类上包含一个私有的最终字段。该字段对类之外的任何东西都不可见,但是如果您用@JsonProperty("@type")(或"@class"或任何您的类型字段的名称)注释它,杰克逊将序列化它,而不管对象位于哪里。

@JsonTypeName("dog")
public static class Dog implements Animal {
  @JsonProperty("@type")
  private final String type = "dog";
  private String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}
廉志强
2023-03-14

这里和这里讨论了造成这种情况的各种原因。我不一定同意这些原因,但由于类型擦除,Jackson根本不知道列表(或集合映射)包含的元素类型。它选择使用不解释注释的简单序列化程序。

在这些链接中,您有两个建议选项:

首先,您可以创建一个实现List的类

class CatList implements List<Cat> {...}

泛型类型参数Cat没有丢失。杰克逊有权使用它。

其次,您可以实例化并使用类型List的ObjectWriter

System.out.println(new ObjectMapper().writerFor(new TypeReference<List<Cat>>() {}).writeValueAsString(list));

将打印

[{"@type":"cat","name":"heyo"}]

 类似资料:
  • 问题内容: 杰克逊(Jackson)做的事情确实很奇怪,我找不到任何解释。我正在执行多态序列化,当对象独立时它可以完美地工作。但是,如果将同一个对象放入列表中并对其进行序列化,则会删除类型信息。 它丢失类型信息的事实将导致人们怀疑类型擦除。但这是在列表 内容 的序列化过程中发生的。Jackson要做的只是检查要序列化的当前对象以确定其类型。 我使用杰克逊2.5.1创建了一个示例: 这是输出: 如您

  • 我想为我们的REST API实现一个自定义的反序列化器,它不仅被Java应用程序使用。因此,我不想让Jackson将类型信息放入序列化的JSON中。 我目前正在努力反序列化<code>CollectionExpand</code>,因为它包含特定<code>ResourceModel</code>的<code>数据</code>列表。 < code>ResourceModel是一个接口,每个< c

  • 我正在使用Jackson 2.1.4将POJO序列化为JSON,但我想忽略序列化的特定字段。我使用了瞬态,但它仍然在序列化该元素。 我正在序列化如下: 请不要建议,因为我不想将我的模型与杰克逊特定的注释联系起来。是否可以仅使用瞬态完成?对象映射器上是否有任何用于可见性设置的 API?

  • 我对Jackson和类型层次结构有以下问题。我正在序列化一个类SubA,该类将扩展为一个字符串,然后尝试将其反序列化回来。当然,在编译时,系统不知道它是基还是SubA,因此我希望它是基,如果它是SubA,则会在编译后执行一些其他操作。 我的基本类看起来像: ...和一个派生自的类: ... 我试图执行以下代码: String是: 但我一次又一次地犯同样的错误。映射器现在知道如何处理另一个类参数-它

  • 我试图在Java中序列化,但在运行我的程序时,我收到一个。 查看类,我注意到它没有实现。 为什么不实现?

  • 我有一个JSON字符串,它将空列表标记为而不是。因此,例如,如果我有一个没有子对象的对象,我将收到这样的字符串: 我想将其反序列化为父类,将子类正确设置为子类的空列表。 对于上面的JSON字符串,我想要一个将其id设置为13的对象,并将子元素设置为新的ArrayList 我知道如何为整个类使用注释 然后呢 但是,我想解决从字符串正确实例化列表的一般问题: 我能得到这样的东西吗?