Spring Boot如何优雅序列化MonetaryAmount仅展示金额与币种
问题描述
在Spring Boot+MongoDB项目中,使用org.javamoney.moneta的MonetaryAmount类型存储商品价格。当前API返回的JSON中,price字段序列化后包含大量冗余内部属性(如context、factory等),希望仅展示金额(amount)和币种(currency)。
已配置Mongo自定义转换器(MoneyReadConverter、MoneyWriteConverter)解决了数据库存储/读取的问题,但API接口的JSON序列化问题未解决;尝试引入Jackson相关库时出现配置冲突,导致应用崩溃。需要通过注解方式实现该字段的自定义序列化与反序列化。
相关代码如下:
ProductDto
@Data @Builder public class ProductDto { @Id private String id; private String name; private String description; private Category category; private List<Variation> variations; private MonetaryAmount price; private Double vat; private boolean active; }
MongoDB配置
@Configuration public class MongoConfiguration { @Bean public MongoCustomConversions mongoCustomConversions() { return new MongoCustomConversions( List.of( new MoneyReadConverter(), new MoneyWriteConverter() ) ); } }
MoneyReadConverter
package com.www.converter; import javax.money.MonetaryAmount; import org.javamoney.moneta.Money; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.ReadingConverter; @ReadingConverter public class MoneyReadConverter implements Converter<String, MonetaryAmount> { @Override public MonetaryAmount convert(String moneyString) { return Money.parse(moneyString); } }
MoneyWriteConverter
package com.www.converter; import javax.money.MonetaryAmount; import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.WritingConverter; @WritingConverter public class MoneyWriteConverter implements Converter<MonetaryAmount, String> { @Override public String convert(MonetaryAmount money) { return money.toString(); } }
Controller
@GetMapping Flux<CatalogDto> getCatalogs() { return catalogService.getAll(); }
Service
@Override public Flux<CatalogDto> getAll() { Flux<Catalog> catalogFlux = catalogRepository.findAll(); return catalogFlux .map( CatalogMapper::mapToCatalogDto ) .switchIfEmpty( Flux.empty()); }
依赖
<dependency> <groupId>org.javamoney</groupId> <artifactId>moneta</artifactId> <version>1.1</version> </dependency>
解决方案
无需引入额外Jackson扩展库,通过自定义序列化器/反序列化器结合字段注解实现需求,避免配置冲突。
1. 自定义Jackson序列化器
创建MonetaryAmountSerializer,将MonetaryAmount序列化为仅包含amount和currency的JSON结构:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import javax.money.MonetaryAmount; import java.io.IOException; public class MonetaryAmountSerializer extends JsonSerializer<MonetaryAmount> { @Override public void serialize(MonetaryAmount monetaryAmount, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeStartObject(); jsonGenerator.writeNumberField("amount", monetaryAmount.getNumber().doubleValue()); jsonGenerator.writeStringField("currency", monetaryAmount.getCurrency().getCurrencyCode()); jsonGenerator.writeEndObject(); } }
2. 自定义Jackson反序列化器
创建MonetaryAmountDeserializer,解析前端传入的精简JSON结构为MonetaryAmount对象:
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.JsonNode; import org.javamoney.moneta.Money; import javax.money.MonetaryAmount; import java.io.IOException; public class MonetaryAmountDeserializer extends JsonDeserializer<MonetaryAmount> { @Override public MonetaryAmount deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); double amount = node.get("amount").asDouble(); String currencyCode = node.get("currency").asText(); return Money.of(amount, currencyCode); } }
3. 给DTO字段添加注解
修改ProductDto的price字段,指定自定义序列化器和反序列化器:
@Data @Builder public class ProductDto { @Id private String id; private String name; private String description; private Category category; private List<Variation> variations; @JsonSerialize(using = MonetaryAmountSerializer.class) @JsonDeserialize(using = MonetaryAmountDeserializer.class) private MonetaryAmount price; private Double vat; private boolean active; }
4. 关键注意事项
- 不要引入
jackson-datatype-money等第三方Jackson扩展,避免和Spring Boot默认Jackson配置冲突; - 确保
moneta依赖版本与Spring Boot版本兼容,若出现依赖冲突,可通过<exclusions>排除冲突的子依赖; - 若使用MapStruct等映射工具,
MonetaryAmount类型可直接在实体与DTO间映射,无需额外配置(Mongo转换器负责数据库与实体的转换,Jackson注解负责API序列化)。
内容的提问来源于stack exchange,提问作者cyril
相关产品推荐
相关产品推荐

