
即 使用纯文本来存储数据。为什么呢?因为这样可以使数据具有最大的可移植性。
在使用 Apache POI 来生成 Excel( github /bingoohuang/excel2javabeans) 时,当修改 Excel 表单的名字时,导致保存后的 Excel 打开,图标数据丢失,打开报告错误:


原因是图表数据中有对 Excel 文件名的引用:

原因找到了,可是怎么去修复呢,完全不知道 POI 图表的哪个 API 能设置这个啊。这时候连万能的谷歌都不好使了,毕竟这个是非常小众的场景啊。
简单尝试了去修正图标对表单名的引用,但是因为找不到正确的 API,都无功而返。只好先把精力放在业务相关上,暂时不允许表单改名了,周末在家里想着,XLSX 不就是 OOXML 格式么,其实就是一个普通的 zip 格式啊,完全可以解压开来,然后去看看图表数据中对于表单名的引用是存储在哪里的。

然后全文搜索一下,马上就找到了位置了:

用 在线XML工具( jsonformatter 组织网/xml-formatter) 分析一下 chart1.xml 的文档结构,如下:

发现文档名的位置在:chart->plotArea->barChart->ser->val->numRef->f 下面。然后就顺藤摸瓜,很快找到了对应的 API 了,成功完成表单名的改名动作。
/**
* 修正图表中对于表单名字的引用。
*
* @param sheet EXCEL表单。
* @param oldSheetName 旧的表单名字。
* @param newSheetName 新的表单名字。
*/
public static void fixChartSheetNameRef(Sheet sheet, String oldSheetName, String newSheetName) {
val drawing = sheet.getDrawingPatriarch();
if (!(drawing instanceof XSSFDrawing)) return;
for (val chart : ((XSSFDrawing) drawing).getCharts()) {
for (val barChart : chart.getCTChart().getPlotArea().getBarChartList()) {
for (val ser : barChart.getSerList()) {
val val = ser.getVal();
if (val == null) continue;
val numRef = val.getNumRef();
if (numRef == null) continue;
val f = numRef.getF();
if (f == null) continue;
if (f.contains(oldSheetName)) {
numRef.setF(f.replace(oldSheetName, newSheetName));
}
}
}
}
}
因为刚刚回看了 Linux / Unix 设计思想,想到 XML 其实也是 FLAT 原则的一种体现。如果不是 OOXML 的格式,而是以前的 xls,那么就没那么容易肉眼去分析格式,去寻找对应的 API 了。

