Как настроить Jackson ObjectMapper, чтобы разрешить NaN в Spring Boot 2.0?

#spring-boot #jackson

#spring-boot #джексон

Вопрос:

Перенесите это сюда с GitHub, поскольку команда Spring использует проблемы GitHub только для ошибок и запросов функций.

Согласно документации Spring Boot, должна быть возможность настроить Jackson ObjectMapper с использованием свойств среды (например, в application.properties ), таких как spring.jackson.parser.<feature_name> , до тех пор, пока вы не определяете свой собственный ObjectMapper компонент.

Мне нужно активировать ALLOW_NON_NUMERIC_NUMBERS функции синтаксического анализатора, поскольку я получаю (строго говоря, недопустимый) JSON со NaN значениями для полей с плавающей запятой, которые я хочу, чтобы Jackson отображал java.lang.Double.NaN в Java.

Итак, в моем, application.properties я добавил spring.jackson.parser.ALLOW_NON_NUMERIC_NUMBERS=true , и я вижу, что это подбирается:

  • Spring Boot JacksonAutoConfiguration создает Jackson2ObjectMapperBuilder
  • Jackson2ObjectMapperBuilder StandardJackson2ObjectMapperBuilderCustomizer выбирает мое spring.jackson.parser.ALLOW_NON_NUMERIC_NUMBERS=true свойство и добавляет его к своей features карте
  • Jackson2ObjectMapperBuilder build() Метод в конечном итоге вызывает configureFeature , в результате чего значение маски ALLOW_NON_NUMERIC_NUMBERS функции (512) добавляется к _parserFeatures значению в JsonFactory ObjectMapper
  • ObjectMapper вводимая в мой компонент с помощью @Autowired также имеет ALLOW_NON_NUMERIC_NUMBERS включенную функцию

Неясно, почему я все еще получаю следующую ошибку Jackson при разборе JSON, которая имеет NaN значение для поля с плавающей запятой: JSON decoding error: Character N is neither a decimal digit number, decimal point, nor "e" notation exponential mark.

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

Комментарии:

1. Вместо использования BigDecimal в качестве типа вы можете использовать Number . При включении DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS функции Jackson будет создан BigDecimal экземпляр для чисел с плавающей запятой. В коде вы можете создать дополнительный геттер getValueAsBigDecimal и преобразовать Number экземпляр в BigDecimal или привести. В случае NaN это будет Decimal экземпляр, который вы можете вернуть null при необходимости.

Ответ №1:

«Проблема» в том, что я пытаюсь сопоставить значения с плавающей точкой с BigDecimal в Java, но BigDecimal не имеет представления для NaN (или (-)Inf в этом отношении). Проблема возникает в том, com.fasterxml.jackson.databind.util.TokenBuffer.Parser что в public BigDecimal getDecimalValue() делает:

     return BigDecimal.valueOf(n.doubleValue());
  

который в конечном итоге (в java.match.BigDecimal ) преобразует значение double в строку "NaN" и которое затем передается в BigDecimal конструктор, который отклоняет его с помощью NumberFormatException и сообщения об ошибке, которое я упомянул в вопросе:

 throw new NumberFormatException("Character "   c
      " is neither a decimal digit number, decimal point, nor"
      " "e" notation exponential mark.");
  

В моем случае я был бы рад, если бы меня NaN сопоставили с null , но я понимаю, что это неправильное поведение для всех, использующих Jackson, поэтому я написал пользовательский десериализатор, чтобы сделать именно это:

 import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.std.NumberDeserializers;

import java.io.IOException;
import java.math.BigDecimal;

public class NaNSafeBigDecimalDeserializer extends JsonDeserializer<BigDecimal> {

    private BigDecimal nanValue = null;

    @Override
    public BigDecimal deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        if (p.isNaN()) {
            return nanValue;
        } else {
            return NumberDeserializers.BigDecimalDeserializer.instance.deserialize(p, ctxt);
        }
    }
}
  

Теперь я могу просто комментировать свои BigDecimal поля с помощью @JsonDeserialize(using = NaNSafeBigDecimalDeserializer.class) .