Как использовать пользовательский JsonSerializer условно через аннотацию

Я написал собственный JsonSerializer для преобразования BigDecimal в String. Я хочу вызвать этот сериализатор, используя аннотацию @JsonSerialize, но условно, т.е. если конкретное логическое значение истинно, только тогда это преобразование BigDecimal в String не должно выполняться иначе.

У меня есть POJO с полем BigDecimal цена. Этот POJO отправляется в ответ на два вызова отдыха:

  • ожидает поле цены как числовое значение // поэтому @JsonSerialize должен не работать
  • ожидает поле цены как строковое значение // поэтому @JsonSerialize должен бежать

Может ли кто-нибудь подсказать, как я могу этого добиться?

Ниже приведен фрагмент кода пользовательского сериализатора, который я написал:

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

Заранее спасибо !!

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
0
3 050
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Для этого вы можете реализовать 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. Для одной конечной точки отдыха я хочу, чтобы это поле цены было отправлено как есть, то есть числовое значение. Для другой конечной точки я хочу, чтобы она отправлялась в строковом формате. Где я могу указать этот логический флаг, чтобы проверить, когда преобразовывать в String, а когда ничего не делать?

G.G. 09.09.2018 12:59

Чтобы добавить, класс Price имеет и другие поля (Integer, BigDecimal, String и т. д.). Итак, как я могу указать, что только одно поле сериализуется как String? А где логика записать значение BigDecimal как String?

G.G. 09.09.2018 13:24

1.Разместите @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

Check if field is "price" :
if not, serialize normally and return
if so, check your boolean field to see if you sould serialize or not

    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. Для одной конечной точки отдыха я хочу, чтобы это поле цены было отправлено как есть, то есть числовое значение. Для другой конечной точки я хочу, чтобы она отправлялась в строковом формате. Где я могу указать этот логический флаг, чтобы проверить, когда преобразовывать в String, а когда ничего не делать?

G.G. 09.09.2018 12:58

toSerializeOrNot = ((MyDtoWithFilter) pojo) .getYourCoditionalField (); в этой строке вы проверяете свой логический флаг в классе Price. обратите внимание, что ваш флаг должен быть в классе Price, и на основе этого вы можете решить сериализовать его в строку или ничего не делать

MohammadReza Alagheband 09.09.2018 13:23

Чтобы добавить, класс Price имеет и другие поля (Integer, BigDecimal, String и т. д.). Итак, как я могу указать, что только одно поле сериализуется как String? А где логика записать значение BigDecimal как String?

G.G. 09.09.2018 13:24

см. этот код пытается установить условие для вашего поля цены в классе Price, который вы аннотируете с помощью @JsonSerialize (using = BigDecimalToStringSerializer.class), это означает, что по умолчанию вы хотите, чтобы он был сериализован в строку. тогда мы делаем условие для другого поля, например. ((MyDtoWithFilter) pojo) .getYourCoditionalField (), чтобы проверить, разрешено ли использовать BigDecimalToStringSerializer для строкового преобразования или нет, и оставить другие поля как есть

MohammadReza Alagheband 09.09.2018 13:30

см. этот код пытается установить условие для вашего поля цены в классе Price, который вы аннотируете с помощью @JsonSerialize (using = BigDecimalToStringSerializer.class), это означает, что по умолчанию вы хотите, чтобы он был сериализован в строку. тогда мы делаем условие для другого поля, например. ((MyDtoWithFilter) pojo) .getYourCoditionalField (), чтобы проверить, разрешено ли использовать BigDecimalToStringSerializer для строкового преобразования или нет, и оставить другие поля нетронутыми.

MohammadReza Alagheband 09.09.2018 13:30

Привет, а что если я хочу наоборот? то есть по умолчанию поля цены должны оставаться в числовом формате при отправке по сети. Но в некоторых случаях его нужно преобразовать в String? Не могли бы вы помочь с этим?

G.G. 11.09.2018 04:39

Я понял, что предоставленное вами решение нарушает мой код. Мое требование простое: 1) если значение флага истинно, не выполняйте пользовательскую сериализацию, т.е. BigDecimal должен оставаться как есть. 2) если значение флага равно false, выполнить настраиваемую сериализацию, т.е. преобразовать BigDecimal в String. Не могли бы вы помочь с этим?

G.G. 11.09.2018 07:44

Я нашел решение своей проблемы. Единственное, что мне нужно было сделать, это: поставить отметку в классе BigDecimalToStringSerilizer.

G.G. 11.09.2018 08:12
Ответ принят как подходящий

Единственное изменение требуется в пользовательском классе сериализатора:

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

}

Другие вопросы по теме