【问题标题】:How to customize Jackson ObjectMapper to allow NaN in Spring Boot 2.0?如何自定义 Jackson ObjectMapper 以在 Spring Boot 2.0 中允许 NaN?
【发布时间】:2021-01-02 01:35:58
【问题描述】:

将这个从 GitHub 移到这里,因为 Spring 团队仅将 GitHub 问题用于错误和功能请求。

根据 Spring Boot documentation,应该可以使用环境属性(例如在 application.properties 中)自定义 Jackson ObjectMapper,例如 spring.jackson.parser.<feature_name>,只要您不定义自己的 ObjectMapper豆子。

我需要激活 ALLOW_NON_NUMERIC_NUMBERS 解析器功能,因为我正在获取带有 NaN 浮点字段值的 JSON(严格来说是无效的),我希望 Jackson 在 Java 中映射到 java.lang.Double.NaN

所以在我的application.properties 中,我添加了spring.jackson.parser.ALLOW_NON_NUMERIC_NUMBERS=true,我可以看到它正在被接收:

  • Spring Boot 的 JacksonAutoConfiguration 正在创建一个 Jackson2ObjectMapperBuilder
  • Jackson2ObjectMapperBuilderStandardJackson2ObjectMapperBuilderCustomizer 正在获取我的 spring.jackson.parser.ALLOW_NON_NUMERIC_NUMBERS=true 属性并将其添加到其 features 地图中
  • Jackson2ObjectMapperBuilderbuild() 方法最终调用 configureFeature,这导致 ALLOW_NON_NUMERIC_NUMBERS 功能 (512) 的掩码值被添加到 ObjectMapperJsonFactory 中的 _parserFeatures
  • ObjectMapper 使用 @Autowired 注入到我的 bean 中也启用了 ALLOW_NON_NUMERIC_NUMBERS 功能

尚不清楚为什么我在解析具有浮点字段 NaN 值的 JSON 时仍然收到以下 Jackson 错误: JSON decoding error: Character N is neither a decimal digit number, decimal point, nor "e" notation exponential mark.

我现在正在调试,所以我可能最终会回答我自己的问题。以上细节可能会帮助来自the GitHub issue 的人找到一个线程,以防他们的功能标志未被应用。

【问题讨论】:

  • 您可以使用Number,而不是使用BigDecimal 作为类型。当您启用 DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS 功能时,Jackson 将为浮点数创建 BigDecimal 实例。在代码中,您可以创建额外的 getter getValueAsBigDecimal 并将 Number 实例转换为 BigDecimal 或强制转换。如果是NaN,它将是一个Decimal 实例,如果需要,您可以返回null

标签: spring-boot jackson


【解决方案1】:

“问题”是我试图在 Java 中将浮点值映射到 BigDecimal,但 BigDecimal 没有代表 NaN(或 (-)Inf 就此而言)。问题出在com.fasterxml.jackson.databind.util.TokenBuffer.Parser 中,public BigDecimal getDecimalValue() 确实如此:

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

最终(在 java.match.BigDecimal 中)将双精度值转换为字符串 "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);
        }
    }
}

现在我可以用@JsonDeserialize(using = NaNSafeBigDecimalDeserializer.class) 注释我的BigDecimal 字段。

【讨论】:

    猜你喜欢
    • 2017-11-07
    • 2017-01-08
    • 2016-01-28
    • 2014-02-23
    • 2011-07-12
    • 1970-01-01
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多