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

42

我想序列化一个不在我的控制下的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技术忽略来自超类的特定字段,例如:如何告诉jackson忽略我无法控制源代码的属性?。但问题在于,如果基类发生更改(例如,其中出现一个新的getAnything()方法),它可能会破坏我的实现。 - 实现自定义序列化程序并在那里处理问题。对我来说,这似乎有点过度。 - 偶然地,我有一个描述我想要序列化的属性的接口,也许我可以混合使用@JsonSerialize(as=IMyGenerated.class)注释...?我能用这个来达到我的目的吗?

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

提前致谢。

5个回答

25

您可以注册自定义的Jackson注释内省器,它将忽略来自特定超类型的所有属性。以下是示例:

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));
    }

}

输出:

{ "field2" : "b" }


3
这个解决方案对我很有效。为了避免复杂的 JOOQ 基类,我的 hasIgnoreMarker 实现是这样的:return m.getDeclaringClass().getName().contains("org.jooq") || super.hasIgnoreMarker(m) - Nathan

21

你也可以使用这个,而不是无必要的覆盖

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

这是更好的解决方案,因为final方法无法被覆盖。 - CrazedCoder

17
你可以重写父类方法,使用 @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方法。 - Jordan Mackie

3

继承的好处在于子类可以扩展或添加功能。因此,通常的方法是将数据序列化。

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

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

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

-3
在你的基类中添加以下注释:
@JsonInclude(Include.NON_NULL)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接