自定义 jackson bean 序列化程序,可以根据兄弟姐妹的值动态忽略一些带注释的属性 属性

Custom jackson bean serializer that can dynamically ignore some annotated properties based on the value of a sibling property

所以我有一个名为 MyClass 的 java 类型,它有一些要序列化的属性。它还有另一个 属性 决定其他属性是否应该序列化。是否可以编写自定义序列化程序来实现此规则,即仅当 java 类型的属性符合条件时才会序列化它们?例如我有另一个兄弟姐妹 属性,它包含一个 ID 列表。如果当前要序列化的 属性 分配了一个 id(通过注释),它是该列表的一部分,那么我不想序列化它。

我的javaclass...

public class MyClass implements WithFieldsToHide {

  // serializable fields

  @FieldId(id = "one")
  private String name;

  @FieldId(id = "two")
  private String company;

  public String getName() { return name; }

  public String getCompany() { return company; }


  // internal fields, non-serializable

  @JsonIgnore
  private Set<String> fieldsToHide;

  public Set<String> getFieldsToHide() { return fieldsToHide; }

}

支持隐藏字段的简单界面...

public interface WithFieldsToHide {
  Set<String> getFieldsToHide();
}

简单的注解...

@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface FieldId {
  String id();
}

用法...

public static void main(String args...) {
  ObjectMapper objectMapper = new ObjectMapper();

  MyClass testObject = new MyClass();

  // assume the setters are available
  testObject.setName("Jackson");
  testObject.setCompany("NoBitMedia");
  testObject.setFieldsToHide(new HashSet<String>() {{ add("two"); }});

  String serialized = objectMapper.writeValueAsString(testObject);
  // serialized should equal `{"name":"Jackson"}`
}

我尝试过的一些东西:

在我尝试深入研究 jackson 代码之前,想问问这种动态序列化是否完全可行..或者有什么建议可以让我得到类似的结果?

虽然我之前确实尝试过覆盖 BeanPropertyWriter,但无法真正弄清楚如何做我想做的事情。

这是我最后做的。

当 bean class 需要动态隐藏它的字段时要实现的接口..

public interface WithFieldsToHide {
  boolean hide(String fieldId);
}

在需要隐藏的字段上添加注释..

@Target(value = { ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
public @interface HideFieldId {
  String id();
}
public class CustomFieldHideModule extends Module {
  @Override
  public void setupModule(SetupContext context) {
    context.addBeanSerializerModifier(new CustomFieldHideSerializerModifier());
  }

  private static class CustomFieldHideSerializerModifier extends BeanSerializerModifier {

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
      // only try to hide fields for class of a particular interface type
      if (WithFieldsToHide.class.isAssignableFrom(beanDesc.getBeanClass())) {
        for (int i = 0; i < beanProperties.size(); i++) {
          BeanPropertyWriter writer = beanProperties.get(i);
          // to be able to run the logic to hide, we need to know if the property has a field id assigned
          HideFieldId annotation = writer.getAnnotation(HideFieldId.class);
          if (null != annotation) {
            beanProperties.set(i, new ConditionalPropertyWriter<>(writer, annotation.id()));
          }
        }
      }
      return beanProperties;
    }

    private static class ConditionalPropertyWriter<T extends WithFieldsToHide> extends BeanPropertyWriter{

      private final fieldId;
      protected ConditionalPropertyWriter(BeanPropertyWriter writer, String fieldId) {
        super(writer);
        this.fieldId = fieldId;
      }

      @Override
      public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider prov) throws Exception {
        T withFieldsToHide = (T) bean; // don't need type check as we're checking when registering this writer for the property
        if (!withFieldsToHide.isInternal(fieldId)) {
          super.serializeAsField(bean, jgen, prov);
        }
      }
    }
  }
}

然后您只需将此模块注册到您的对象映射器,瞧!

我还想要多一层隐藏,对于地图 属性 类型,我只需要隐藏地图 属性 中的某些元素而不是整个 属性 本身,并且可以写一些东西来实现它。让我知道是否有人想要它,为简洁起见不包括在内。

谢谢!!