如何在Jackson CsvMapper中将小数点替换为逗号?
问题
使用Spring Boot结合Jackson生成CSV文件时,Double类型字段默认以点(.)作为小数分隔符,需要替换为逗号(,)。尝试将Double字段改为String类型后,导出时字段会被自动添加双引号,不符合需求。期望生成的CSV中,数字字段无引号且使用逗号作为小数分隔符,格式如下:
field1;field2;field3;field4 "Value 1";"Value, 2";7,1;8,0
解决方案
以下两种方法可解决该问题,推荐优先使用Locale配置的方式,更符合Jackson原生设计:
方法一:通过Locale配置小数分隔符
Jackson的数字格式化逻辑依赖Locale,设置支持逗号作为小数分隔符的Locale(如德语区Locale),同时在CSV Schema中指定数字列类型,即可避免添加引号并替换小数分隔符。
修改CSV生成代码
替换原有的JsonNode转换逻辑,直接用CsvMapper处理实体类,并配置Locale和列类型:
CsvMapper csvMapper = new CsvMapper(); // 设置为对应Locale,例如德语区Locale(小数分隔符为逗号),可替换为你所在国家的Locale csvMapper.setLocale(new Locale("de", "DE")); // 构建CSV Schema,明确指定数字列类型 CsvSchema.Builder csvSchemaBuilder = CsvSchema.builder(); csvSchemaBuilder.addColumn("field1"); csvSchemaBuilder.addColumn("field2"); csvSchemaBuilder.addColumn("field3", CsvSchema.ColumnType.NUMBER); csvSchemaBuilder.addColumn("field4", CsvSchema.ColumnType.NUMBER); CsvSchema csvSchema = csvSchemaBuilder.build().withHeader().withColumnSeparator(';'); // 直接序列化实体类列表(responseObject应为List<ExampleQuestionClass>) dataCsv = csvMapper.writerFor(List.class) .with(csvSchema) .writeValueAsBytes(responseObject);
说明
- 配置Locale后,Jackson会自动按照该区域的数字格式输出,将小数点替换为逗号。
- 指定
ColumnType.NUMBER后,CSV生成时不会为数字字段添加双引号,符合期望格式。
方法二:自定义Double序列化器
如果需要更精细的格式控制(如固定小数位数),可自定义Jackson序列化器处理Double字段,直接输出带逗号分隔符的字符串,并通过Schema配置避免引号。
步骤1:创建自定义序列化器
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; import java.io.IOException; import java.text.DecimalFormat; public class CustomDoubleSerializer extends StdSerializer<Double> { // 自定义格式,保留两位小数 private static final DecimalFormat df = new DecimalFormat("#.##"); public CustomDoubleSerializer() { this(null); } public CustomDoubleSerializer(Class<Double> t) { super(t); } @Override public void serialize(Double value, JsonGenerator gen, SerializerProvider provider) throws IOException { if (value != null) { // 将小数点替换为逗号 String formattedValue = df.format(value).replace('.', ','); gen.writeString(formattedValue); } } }
步骤2:在实体类字段上添加注解
@Data @NoArgsConstructor @AllArgsConstructor @JsonPropertyOrder({"field1", "field2", "field3", "field4"}) public class ExampleQuestionClass { @JsonProperty("field1") private String field1; @JsonProperty("field2") private String field2; @JsonProperty("field3") @JsonSerialize(using = CustomDoubleSerializer.class) private Double field3; @JsonProperty("field4") @JsonSerialize(using = CustomDoubleSerializer.class) private Double field4; }
步骤3:配置CSV Schema并序列化
CsvMapper csvMapper = new CsvMapper(); // 构建Schema时指定数字列类型,避免字符串引号 CsvSchema.Builder csvSchemaBuilder = CsvSchema.builder(); csvSchemaBuilder.addColumn("field1"); csvSchemaBuilder.addColumn("field2"); csvSchemaBuilder.addColumn("field3", CsvSchema.ColumnType.NUMBER); csvSchemaBuilder.addColumn("field4", CsvSchema.ColumnType.NUMBER); CsvSchema csvSchema = csvSchemaBuilder.build().withHeader().withColumnSeparator(';'); dataCsv = csvMapper.writerFor(List.class) .with(csvSchema) .writeValueAsBytes(responseObject);
注意事项
- 避免先将实体类序列化为JsonNode再转CSV:此过程中Double会被转为带点的字符串,后续无法通过Locale或Schema修改格式。
- 选择对应Locale时,需确保该区域的小数分隔符为逗号,可根据实际需求替换为
Locale.FRANCE、Locale.ITALY等。
内容的提问来源于stack exchange,提问作者Inazense
相关产品推荐
相关产品推荐

