自定义 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"}`
}
我尝试过的一些东西:
我可以扩展 BeanSerializerModifier
并覆盖 modifySerializer()
,但我没有可用的实际对象来读取 fieldsToHide
属性(据此决定是否连载)
我可以写一个自定义转换器(扩展StdConverter<MyClass, MyClass>
,但我没有convert()
方法中注释的属性列表。
我可以编写一个自定义序列化程序 (MyClassSerializer<T extends WithFieldsToHide> extends JsonSerializer<T>
),但在这里,我也不确定我是否可以使用 属性 列表及其注释serialize()
方法。
在我尝试深入研究 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);
}
}
}
}
}
然后您只需将此模块注册到您的对象映射器,瞧!
我还想要多一层隐藏,对于地图 属性 类型,我只需要隐藏地图 属性 中的某些元素而不是整个 属性 本身,并且可以写一些东西来实现它。让我知道是否有人想要它,为简洁起见不包括在内。
谢谢!!
所以我有一个名为 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"}`
}
我尝试过的一些东西:
我可以扩展
BeanSerializerModifier
并覆盖modifySerializer()
,但我没有可用的实际对象来读取fieldsToHide
属性(据此决定是否连载)我可以写一个自定义转换器(扩展
StdConverter<MyClass, MyClass>
,但我没有convert()
方法中注释的属性列表。我可以编写一个自定义序列化程序 (
MyClassSerializer<T extends WithFieldsToHide> extends JsonSerializer<T>
),但在这里,我也不确定我是否可以使用 属性 列表及其注释serialize()
方法。
在我尝试深入研究 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);
}
}
}
}
}
然后您只需将此模块注册到您的对象映射器,瞧!
我还想要多一层隐藏,对于地图 属性 类型,我只需要隐藏地图 属性 中的某些元素而不是整个 属性 本身,并且可以写一些东西来实现它。让我知道是否有人想要它,为简洁起见不包括在内。
谢谢!!