Java 到 Jackson JSON 序列化:Money 字段
Posted
技术标签:
【中文标题】Java 到 Jackson JSON 序列化:Money 字段【英文标题】:Java to Jackson JSON serialization: Money fields 【发布时间】:2012-07-04 09:19:37 【问题描述】:目前,我正在使用 Jackson 从基于 Spring 的 Web 应用程序发送 JSON 结果。
我遇到的问题是试图让所有货币字段以 2 位小数输出。我无法使用 setScale(2)
解决这个问题,因为像 25.50 这样的数字会被截断为 25.5 等
还有其他人处理过这个问题吗?我正在考虑使用自定义 Jackson 序列化程序制作 Money 类...您可以为字段变量制作自定义序列化程序吗?您可能可以...但即便如此,我怎样才能让我的客户序列化程序将数字添加为带 2 位小数的数字?
【问题讨论】:
您将这些值存储在什么位置?BigDecimal
?
【参考方案1】:
您可以在资金字段中使用自定义序列化程序。这是一个使用 MoneyBean 的示例。 amount 字段使用 @JsonSerialize(using=...) 进行注释。
public class MoneyBean
//...
@JsonProperty("amountOfMoney")
@JsonSerialize(using = MoneySerializer.class)
private BigDecimal amount;
//getters/setters...
public class MoneySerializer extends JsonSerializer<BigDecimal>
@Override
public void serialize(BigDecimal value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonProcessingException
// put your desired money style here
jgen.writeString(value.setScale(2, BigDecimal.ROUND_HALF_UP).toString());
就是这样。 BigDecimal 现在以正确的方式打印。我用一个简单的测试用例来展示它:
@Test
public void jsonSerializationTest() throws Exception
MoneyBean m = new MoneyBean();
m.setAmount(new BigDecimal("20.3"));
ObjectMapper mapper = new ObjectMapper();
assertEquals("\"amountOfMoney\":\"20.30\"", mapper.writeValueAsString(m));
【讨论】:
不错的方法,但它会将其打印为字符串——而不是 JSON 输出中的数字类型。 从业务角度来看,这是一种糟糕的方法。金钱不应该在序列化时四舍五入。如果您想填充尾随零(与盲目设置比例不同),那么您必须在没有ROUND_HALF_UP
的情况下这样做。此外,不同的货币需要不同的尾随小数位数。
jro,如果在 Steve 的 serialize() 中使用 writeNumber() 而不是 writeString(),那么该字段将在 JSON 中显示为数字。
@PeterDavis 是和否。这真的取决于你序列化它的目的。在现实世界中,Money 只有两位小数,例如,如果您的 API 返回 12.4999990008212354,那么实际的现实世界值应该是 12.49 还是 12.50?在税收世界中,他们更喜欢您支付 12.50。
@MaksymBykovskyy 重点不是不应发生舍入,而是这些舍入规则是业务逻辑,不应在序列化时应用它们。它们需要在您进行序列化之前应用,否则您会混淆问题。【参考方案2】:
您可以在BigDecimal
变量上使用@JsonFormat
注释和shape
作为STRING
。参考如下:
import com.fasterxml.jackson.annotation.JsonFormat;
class YourObjectClass
@JsonFormat(shape=JsonFormat.Shape.STRING)
private BigDecimal yourVariable;
【讨论】:
是的,你是对的。此选项自 Jackson 2.9.5 起可用:github.com/FasterXML/jackson/wiki/Jackson-Release-2.9.5 它对我有用。喜欢这个答案。这是一种非常简单和干净的做事方式。 令人惊讶的是它对我不起作用(Spring Boot 2.3.7):( 而接受的答案中的@JsonSerialize
可以。【参考方案3】:
除了在每个成员或 getter 上设置 @JsonSerialize 之外,您还可以配置一个为特定类型使用自定义序列化程序的模块:
SimpleModule module = new SimpleModule();
module.addSerializer(BigInteger.class, new ToStringSerializer());
objectMapper.registerModule(module);
在上面的例子中,我使用 to string serializer 来序列化 BigIntegers(因为 javascript 不能处理这样的数值)。
【讨论】:
在哪里定义客户序列化程序?这正是我想做的,但我不知道该把代码放在哪里。 随心所欲,只需要实现 com.fasterxml.jackson.databind.JsonSerializer 你能再具体一点吗?我还是不知道放哪里。 我试过这个,但它只适用于你试图序列化的原始类。如果对象具有这种类型的属性,则不会使用此序列化程序对其进行序列化(我使用的是 Spring boot 1.3) @DaveH: 如果你的SpringMvcConfiguration
扩展DelegatingWebMvcConfiguration
,那么你可以覆盖extendMessageConverters()
方法,在转换器列表中找到MappingJackson2HttpMessageConverter
,然后像这样注册SimpleModule:mappingJackson2HttpMessageConverter.getObjectMapper().registerModule(simpleModule)
。这样你就不会覆盖现有的mappingJackson2HttpMessageConverter
。【参考方案4】:
我是jackson-datatype-money 的维护者之一,所以请谨慎回答这个问题,因为我肯定有偏见。该模块应该满足您的需求并且它非常轻量级(没有额外的运行时依赖项)。此外,Jackson docs、Spring docs 中也提到了它,甚至还有 some discussions 已经在讨论如何将其集成到杰克逊的官方生态系统中。
【讨论】:
【参考方案5】:我遇到了同样的问题,我将其格式化为 JSON 作为字符串。可能有点小技巧,但很容易实现。
private BigDecimal myValue = new BigDecimal("25.50");
...
public String getMyValue()
return myValue.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
【讨论】:
【参考方案6】:正如Sahil Chhabra 建议的那样,您可以在变量上使用@JsonFormat
和适当的shape
。
如果您想将其应用于 Dto's
中的每个 BigDecimal
字段,您可以覆盖给定类的默认格式。
@Configuration
public class JacksonObjectMapperConfiguration
@Autowired
public void customize(ObjectMapper objectMapper)
objectMapper
.configOverride(BigDecimal.class).setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
【讨论】:
此功能仅适用于 Jackson 版本2.9.5
(github.com/FasterXML/jackson-databind/issues/1911) 我遇到了一个问题,这是我们代码使用的库中的分辨率,但我们的应用程序固定为旧版本的杰克逊。【参考方案7】:
受Steve 的启发,并作为 Java 11 的更新。以下是我们如何进行 BigDecimal 重新格式化以避免科学记数法。
public class PriceSerializer extends JsonSerializer<BigDecimal>
@Override
public void serialize(BigDecimal value, JsonGenerator jgen, SerializerProvider provider) throws IOException
// Using writNumber and removing toString make sure the output is number but not String.
jgen.writeNumber(value.setScale(2, RoundingMode.HALF_UP));
【讨论】:
以上是关于Java 到 Jackson JSON 序列化:Money 字段的主要内容,如果未能解决你的问题,请参考以下文章