json JPA和Jackson与继承-如何让它工作?

h7wcgrx3  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(188)

bounty将在2小时后到期。此问题的答案有资格获得+50声望奖励。user2441441正在寻找一个规范答案

我认为这可以用一个例子更好地解释(代码如下)。假设我有BaseClass和一些子类,如Employee,Contractor等。我不想要BaseClass的数据库表(因此使用@MappedSuperClass)。BaseClass有一些公共字段,但是entityType在任何子类中都不是有效的数据库字段,例如Employee,Contrator(因此我将其表示为@Transient)。现在我得到了一个json,它有所有的Employee值,它没有BaseClass中的endDate值。我想在Employee类中设置BaseClass的公共字段,如endDate,并将其保存到Employee表中。
在检索时,我希望检索所有以endDate作为某个值的Employees。这应该是可能的,因为我在保存员工记录时确实保存了endDate。
附加信息:我搞不懂这个。我使用的是openAPI,我明白我需要这个鉴别器列来生成一个超类-子类关系-使用openapi 3.0从空抽象类继承。

@JsonIgnoreProperties(
  value = "entityType", // ignore manually set entityType, it will be automatically generated by Jackson during serialization
  allowSetters = true // allows the entityType to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "entityType", visible = true)
@JsonSubTypes({
  @JsonSubTypes.Type(value = Employee.class, name = "Employee")
   @JsonSubTypes.Type(value = Contractor.class, name = "Contractor")
}) 
  @jakarta.persistence.MappedSuperclass
  public abstract BaseClass{
   //common to all sub-classes
   LocalDate endDate;

   /**
    Gets or Sets entityType
   */
   public enum EntityTypeEnum {
    Employee("Employee");
    Contractor("Contractor");

    private String value;

    EntityTypeEnum(String value) {
      this.value = value;
    }

    @JsonValue
    public String getValue() {
      return value;
    }

    @Override
    public String toString() {
      return String.valueOf(value);
    }

    @JsonCreator
    public static EntityTypeEnum fromValue(String value) {
      for (EntityTypeEnum b : EntityTypeEnum.values()) {
        if (b.value.equals(value)) {
          return b;
        }
      }
      throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
  }

     @JsonProperty("entityType")
     @jakarta.persistence.Transient
     private EntityTypeEnum entityType;

    }

    public Employee extends BaseClass {...}

    public Contractor extends BaseClass{...}

如果我知道我正在反序列化Employee.class,但我收到的员工的Json根本没有entityType作为属性,那么这种反序列化将如何工作?目前我得到'missing type id property ' entityType ''我想反序列化Employees to List类的json。Json没有entityType或任何超类(即上例中的BaseClass)字段。
所以总结一下,我希望Jackson和JPA能很好地合作。
1.我想让Jackson正确地读取一个json,其中json只有Employee相关的值(还没有超类值)(在运行时,我会知道我有Employee.class)。
1.我想在设置自己的值(如endDate)(在BaseClass中)后将其保存到Employees表中。
1.我想稍后从Employees表中检索它,知道我在运行时有endDate和Employee.class值。
希望这有意义。如果不是,让我重新表达我的问题:OpenAPI要求我使用鉴别器,在本例中我们称之为entityType,以便在生成类时继承工作。但是当我这样做时,Jackson抱怨说在序列化json时找不到属性id entityType。json显然没有名为entityType的字段,因为我们创建了这个字段。我累了variaous approached包括使用:1.

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE);

将以下内容添加到BaseClass的顶部:

@JsonIgnoreProperties(
  value = "entityType", // ignore manually set entityType, it will be automatically generated by Jackson during serialization
  allowSetters = true // allows the entityType to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "entityType")

我仍然得到这个错误com.fasterxml.Jackson.databind.exc.InvalidTypeIdException:无法解析[简单类型,类Employee]的子类型:缺少类型id属性“entityType”

kg7wmglp

kg7wmglp1#

如果我知道我正在反序列化Employee.class,但我收到的员工的Json根本没有entityType作为属性,那么这种反序列化将如何工作?
如果 * 你 * 知道JSON表示Employee而不是Contractor(例如基于源),然后可以调用Map器,传递Employee类。但是如果你在一般情况下使用这个方法,你希望反序列化返回EmployeeContractor,那么就需要一个 discriminator,这样反序列化器就可以自己选择正确的具体类。(考虑两个子类具有相同字段的情况,但仍然被建模为不同的类-序列化器无法猜测它应该示例化哪个类。
(1)我建议您接受在超类中永久添加entityType-特别是您应该序列化它,以便JSON随后携带类型信息。(2)您应该删除@JsonIgnoreProperties...,以便类型info is 可用于反序列化器。
要自动执行正确的类型,您可以执行以下操作:(3)BaseClass应该在其构造函数中要求entityType,您可以在EmployeeContractor构造函数中编写正确的类型,以便它们将类型传递给BaseClass
由于您现在自己管理entityType,我认为(4)@JsonTypeInfo应该是:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "entityType", visible = true)
                                                                    ^^^^^^^^^

这种方法与JPA兼容。我使用Jackson、Spring Data和Mongo作为我的后台存储。在Mongo的情况下,Spring添加了一个_class字段作为处理反序列化的不同方式。_class字段出现在数据库中,但不在java对象中。因此,这种方法使用了两种并行的类型管理方法,它们不会冲突;事实上,如果您需要与后备存储稍微不同的外部(JSON)模型,这允许更细粒度的控制。

相关问题