基于POI-TL实现动态Word模板的数据填充:【散点图】特殊处理方案
基于POI-TL实现动态Word模板的数据填充:散点图特殊处理方案
在使用POI-TL进行Word模板动态数据填充时,图表生成是一个常见需求。最近在项目中使用POI-TL处理散点图时遇到了一个特殊问题,经过研究后找到了解决方案,特此记录分享。
问题背景
POI-TL作为一款优秀的Java Word模板引擎,提供了丰富的图表渲染功能。然而在使用默认的多系列插件渲染散点图时,会出现一个奇怪的错误:当散点图的类别是中文时,POI-TL会把这些中文类别当作x轴数据来渲染,而实际上散点图的x轴需要的是数字索引,这就导致了图表渲染失败。报错截图:
问题分析
通过研究Word图表中散点图的示例数据,我发现:
- 散点图的数据格式与其他图表(如柱状图、折线图)在结构上是一致的
- 都可以包含中文类别
- 关键区别在于:散点图的x轴需要显示数字索引,而其他图表可以直接使用文字类别
默认插件的问题在于没有区分散点图和其他图表的这种差异,统一将类别作为x轴数据处理,当类别是中文时就会出现解析错误。
解决方案
POI-TL提供了灵活的插件扩展机制,允许我们通过实现RenderPolicy接口开发自定义插件。我的解决方案是开发一个专门处理散点图的自定义插件,重写其数据源处理逻辑。
核心思路是:分离"X轴显示标签"和"散点数值坐标"
核心改进说明
-
类别→索引映射:通过
createCategoryIndexMap
方法将中文类别(如"数据分析应用")转为数值索引(1,2,3…),作为散点图的实际X坐标。 -
数据源分离:
- 散点图的X轴数据源使用数值索引(避免字符串解析问题)
- X轴的显示标签仍为中文类别(不影响视觉展示)
-
兼容原有逻辑:继承默认插件的大部分逻辑,仅修改散点图的X轴数据处理,确保与其他图表类型(柱状、折线)兼容。
代码实现
下面是自定义散点图渲染插件的完整实现:
package com.hdxm.server.sample.utils;import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.ChartMultiSeriesRenderData;
import com.deepoove.poi.data.SeriesRenderData;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy;
import com.deepoove.poi.template.ChartTemplate;
import com.deepoove.poi.util.ReflectionUtils;
import org.apache.poi.xddf.usermodel.chart.XDDFAreaChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFChart;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFLineChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFScatterChartData;
import org.apache.poi.xwpf.usermodel.XWPFChart;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;/*** poi-tl在用默认的多系列插件渲染散点图时会报错,它会把类别当做x轴来渲染数据,* 而实际的散点图类别是中文,但图上的x轴是数字索引,这样就会报错,因此要自定义一个插件用来渲染散点图* @author: Hanweihu* @date: 2025/9/12 9:53*/
public class ScatterChartRenderPolicy extends MultiSeriesChartTemplateRenderPolicy {private void validate(List<XDDFChartData> chartSeries, ChartMultiSeriesRenderData data) {// 验证组合图表if (chartSeries.size() >= 2) {long nullCount = data.getSeriesDatas().stream().filter(d -> null == d.getComboType()