基于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()