【问题标题】:How to use custom JsonSerializer conditionally via annotation如何通过注释有条件地使用自定义 JsonSerializer
【发布时间】:2018-09-09 05:18:35
【问题描述】:

我已经编写了一个自定义的 JsonSerializer 来将 BigDecimal 转换为 String。我想使用 @JsonSerialize 注释来调用这个序列化程序,但有条件地,即如果一个特定的布尔值是 true,那么这个 BigDecimal 到 String 的转换应该不做。

我有一个 POJO,它有一个 BigDecimal price 字段。这个 POJO 是为了响应两个 rest 调用而发送的:

  • 期望价格字段为数值 //所以@JsonSerialize 应该 不运行
  • 期望价格字段为字符串值 //so @JsonSerialize 应该运行

谁能建议我如何实现它?

下面是我写的自定义序列化器的sn-p代码:

public class BigDecimalToStringSerializer extends JsonSerializer<BigDecimal> {

    @Override
    public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException,
        JsonProcessingException {
        gen.writeString(value.toString());
    }

}

具有价格字段的 POJO 文件:

JsonInclude(Include.NON_NULL)
public class Price{
    private BigDecimal price;
    public Price() {
    }
    @JsonSerialize(using = BigDecimalToStringSerializer.class)
    public BigDecimal getPrice() {
        return price;
    }   
    public void setPrice(BigDecimal pric) {
        this.price  = price;
    }
}

提前致谢!!

【问题讨论】:

    标签: java json spring jackson


    【解决方案1】:

    您可以为此实现PropertyFilter。首先,您需要在我们的实体上定义过滤器,使用@JsonFilter 注解:

    @JsonFilter("stringValueFilter")
    public class Price {
      private BigDecimal price;
      public Price() {
      }
    
      public BigDecimal getPrice() {
         return price;
      }   
      public void setPrice(BigDecimal pric) {
         this.price  = price;
      }
    }
    

    这是你的PropertyFilter

    public interface PropertyFilter {
        void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer);
    
        void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception;
    
        void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException;
    
        @Deprecated 
        void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) throws JsonMappingException;
    }
    

    第一种方法需要针对您的情况进行特殊实现:

    public class StringValueFilter implements PropertyFilter {
        void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) {
            if (pojo instanceof Price && isValueFieldNumber((Price) pojo)) {
                return; // skip this field
            }
            writer.serializeAsField(pojo, jgen, prov);
        }
    
        private isValueFieldNumber(Price price) {
            return: //check your logic and return
         }
    
        void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider prov, PropertyWriter writer) throws Exception {
            writer.serializeAsField(elementValue, jgen, prov);
        }
    
        void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException {
            writer.depositSchemaProperty(objectVisitor);
        }
    
        @Deprecated 
        void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) throws JsonMappingException {
            writer.depositSchemaProperty(propertiesNode, provider);
        }
    }
    

    此过滤器包含根据其值决定price 字段是否将被序列化的实际逻辑。

    接下来,您需要将此过滤器挂钩到ObjectMapper

    final ObjectMapper mapper = new ObjectMapper();
    final FilterProvider filterProvider = new SimpleFilterProvider()
            .addFilter("stringValueFilter", new StringValueFilter());
    mapper.setFilters(filterProvider);
    

    【讨论】:

    • 在我的 java 代码中,价格字段始终为 BigDecimal。对于一个休息端点,我希望这个价格字段按原样发送,即数值。对于其他端点,我希望它以字符串格式发送。我在哪里可以指定这个布尔标志来检查什么时候转换成字符串,什么时候什么都不做?
    • 补充一下,Price 类还有其他字段(Integers、BigDecimal、String 等)。那么,如何指定只有单个字段被序列化为字符串?将 BigDecimal 值写入 String 的逻辑在哪里?
    【解决方案2】:

    1.Place @JsonFilter 你的价格等级:

    @JsonFilter("myFilter")
    @JsonInclude(Include.NON_NULL)
    public class Price {
        private BigDecimal price;
        public Price() {}
        @JsonSerialize(using = BigDecimalToStringSerializer.class)
        public BigDecimal getPrice() {
            return price;
        }
        public void setPrice(BigDecimal pric) {
            this.price = price;
        }
    }
    

    2.定义CustomFilter逻辑

    检查字段是否为“价格”:
    如果没有,则正常序列化并返回 如果是这样,请检查您的布尔字段以查看您是否可以序列化

        public class CustomFilter extends SimpleBeanPropertyFilter {
        @Override
        public void serializeAsField
            (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
        throws Exception {
            if (include(writer)) {
                if (!writer.getName().equals("price")) {
                    writer.serializeAsField(pojo, jgen, provider);
                    return;
                }
                Boolean toSerializeOrNot = ((MyDtoWithFilter) pojo).getYourCoditionalField();
                if (toSerializeOrNot) {
                    writer.serializeAsField(pojo, jgen, provider);
                }
            } else if (!jgen.canOmitFields()) { 
                writer.serializeAsOmittedField(pojo, jgen, provider);
            }
        }
        @Override
        protected boolean include(BeanPropertyWriter writer) {
            return true;
        }
        @Override
        protected boolean include(PropertyWriter writer) {
            return true;
        }
    }
    

    更多信息

    https://www.baeldung.com/jackson-serialize-field-custom-criteria

    【讨论】:

    • 在我的 java 代码中,价格字段始终为 BigDecimal。对于一个休息端点,我希望这个价格字段按原样发送,即数值。对于其他端点,我希望它以字符串格式发送。我在哪里可以指定这个布尔标志来检查什么时候转换成字符串,什么时候什么都不做?
    • toSerializeOrNot = ((MyDtoWithFilter) pojo).getYourCoditionalField();在这一行中,您检查价格类中的布尔标志。请注意,您的标志应该在 Price 类中,并且基于此您可以决定将其序列化为字符串或什么也不做
    • 补充一下,Price 类还有其他字段(Integers、BigDecimal、String 等)。那么,如何指定只有单个字段被序列化为字符串?将 BigDecimal 值写入 String 的逻辑在哪里?
    • 看到这段代码试图以你在 Price 类中的价格字段为条件,你用 @JsonSerialize(using = BigDecimalToStringSerializer.class) 注释它,这意味着默认情况下你希望它被序列化为字符串.那么我们所做的是我们以另一个领域为条件,例如((MyDtoWithFilter) pojo).getYourCoditionalField() 检查是否允许使用 BigDecimalToStringSerializer 进行字符串化,并保持其他字段不变
    • 看到这段代码试图以你在 Price 类中的价格字段为条件,你用 @JsonSerialize(using = BigDecimalToStringSerializer.class) 注释它,这意味着默认情况下你希望它被序列化为字符串.那么我们所做的是我们以另一个领域为条件,例如((MyDtoWithFilter) pojo).getYourCoditionalField() 检查是否允许使用 BigDecimalToStringSerializer 进行字符串化,而其他字段保持不变。
    【解决方案3】:

    自定义序列化程序类中唯一需要更改:

    public class BigDecimalToStringSerializer extends JsonSerializer<BigDecimal> {
    
        @Override
        public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException,
            JsonProcessingException {
        if (pojo instanceof Price && ((Price) pojo).shouldConvertInString()) {
            gen.writeString(value.toString());
        } else {
            gen.writeNumber(value);
        }
        }
    

    }

    【讨论】:

      猜你喜欢
      • 2016-06-27
      • 1970-01-01
      • 2015-07-13
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 2016-02-12
      • 2017-09-22
      • 1970-01-01
      相关资源
      最近更新 更多