Jackson 序列化:如何忽略超类属性

Posted

技术标签:

【中文标题】Jackson 序列化:如何忽略超类属性【英文标题】:Jackson serialization: how to ignore superclass properties 【发布时间】:2015-01-12 18:21:06 【问题描述】:

我想序列化一个不受我控制的 POJO 类,但想避免序列化来自超类而不是最终类的任何属性。示例:

public class MyGeneratedRecord extends org.jooq.impl.UpdatableRecordImpl<...>,
    example.generated.tables.interfaces.IMyGenerated 
  public void setField1(...);
  public Integer getField1();

  public void setField2(...);
  public Integer getField2();
...

从例子中你可以猜到这个类是由JOOQ生成的,继承自一个复杂的基类UpdatableRecordImpl,它也有一些类似bean属性的方法,这会导致序列化过程中出现问题。另外,我有几个类似的类,所以最好避免为我生成的所有 POJO 重复相同的解决方案。

到目前为止,我已经找到了以下可能的解决方案:

使用 mixin 技术忽略来自超类的特定字段,如下所示:How can I tell jackson to ignore a property for which I don't have control over the source code?

这样做的问题是,如果基类发生变化(例如,其中出现了一个新的 getAnything() 方法),它可能会破坏我的实现。

实现自定义序列化程序并在那里处理问题。这对我来说似乎有点矫枉过正。

顺便说一句,我有一个接口,它准确描述了我想要序列化的属性,也许我可以混合一个 @JsonSerialize(as=IMyGenerated.class) 注释......?我可以将其用于我的目的吗?

但是,从纯设计的角度来看,最好的办法是能够告诉杰克逊我只想序列化最终类的属性,而忽略所有继承的属性。有没有办法做到这一点?

提前致谢。

【问题讨论】:

【参考方案1】:

您可以注册一个自定义Jackson annotation intropector,它将忽略来自某个超类型的所有属性。这是一个例子:

public class JacksonIgnoreInherited 

    public static class Base 
        public final String field1;

        public Base(final String field1) 
            this.field1 = field1;
        
    

    public static class Bean extends Base 
        public final String field2;

        public Bean(final String field1, final String field2) 
            super(field1);
            this.field2 = field2;
        
    

    private static class IgnoreInheritedIntrospector extends JacksonAnnotationIntrospector 
        @Override
        public boolean hasIgnoreMarker(final AnnotatedMember m) 
            return m.getDeclaringClass() == Base.class || super.hasIgnoreMarker(m);
        
    

    public static void main(String[] args) throws JsonProcessingException 
        final ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new IgnoreInheritedIntrospector());
        final Bean bean = new Bean("a", "b");
        System.out.println(mapper
                        .writerWithDefaultPrettyPrinter()
                        .writeValueAsString(bean));
    


输出:

“字段2”:“b”

【讨论】:

这个解决方案对我有用。太忽略复杂的 JOOQ 基类,我的hasIgnoreMarker 实现是return m.getDeclaringClass().getName().contains("org.jooq") || super.hasIgnoreMarker(m)【参考方案2】:

您可以覆盖您希望阻止输出的超类方法并使用@JsonIgnore 对其进行注释。覆盖将属性创建的控制转移到子类,同时使其能够从输出中过滤它。

例如:

public class SomeClass 
  public void setField1(...);
  public Integer getField1();

  public void setField2(...);
  public Integer getField2();

  @Override
  @JsonIgnore
  public String superClassField1(...)
      return super.superClassField1();
  ;

  @Override
  @JsonIgnore
  public String superClassField2(...)
      return super.superClassField2();
  ;
...

【讨论】:

补充@AbhishekChatterjee 的观点,jackson 使用对象中的 getter 来确定要显示的内容,因此只有 getter 需要被覆盖。【参考方案3】:

您也可以使用它来代替不必要的覆盖

@JsonIgnoreProperties( "aFieldFromSuperClass")
public class Child extends Base 
   private String id;       
   private String name; 
   private String category;
 

【讨论】:

【参考方案4】:

继承的好处是子类扩展或添加功能。所以通常的做法是对数据进行序列化。

解决方法是将值对象 (VO) 或数据传输对象 (DTO) 与您需要序列化的字段一起使用。步骤:

使用应序列化的字段创建一个 VO 类。 使用 BeanUtils.copyProperties(target VO, source data) 复制属性 序列化 VO 实例。

【讨论】:

在这种情况下,这将是纯粹的复制粘贴。使用生成的类的重点是避免重复我的模式信息(字段、类型等)。理想情况下,一个字段的名称应该只输入一次——在我的 SQL 模式中(或者在 ORM 的情况下在手写的 POJO 中)。到目前为止,这在我的设计中或多或少是正确的,我不想打破它。 否则需要重新定义子类上的字段,并标记为“不可序列化”。 @Ferenc:您可以让 jOOQ 为您生成所有这些 POJO / VO / DTO:jooq.org/doc/latest/manual/code-generation/codegen-pojos 是的,这是另一种可能的解决方案——我已经使用了 POJO,所以 select 方法上的一个简单的 .into(MyGenerated.class) 也可以做到。谢谢!【参考方案5】:

在您的基类中添加以下注释:

@JsonInclude(Include.NON_NULL)

【讨论】:

以上是关于Jackson 序列化:如何忽略超类属性的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中的 Jackson JSON 反序列化期间忽略丢失的属性

jackson 常用注解,比如忽略某些属性,驼峰和下划线互转

Jackson序列化生成 json 不同场景下忽略字段

详解jackson注解(三)jackson包含属性、忽略属性的注解

Jackson 序列化 - 忽略未设置的值,但提供明确设置为 null 的值

Jackson忽略空字段