如何从剪贴板XML字符串实例化XSSFWorkbook以处理Excel数据?
问题
我正尝试实现一个可将Excel行粘贴到SpreadsheetView控件(ControlsFX)中的功能。目前已成功获取剪贴板对应的XML字符串,内容如下:
<?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> <Styles> <Style ss:ID="Default" ss:Name="Normal"> <Alignment ss:Vertical="Bottom"/> <Borders/> <Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11" ss:Color="#000000"/> <Interior/> <NumberFormat/> <Protection/> </Style> <Style ss:ID="s113"> <Alignment ss:Horizontal="Center" ss:Vertical="Top"/> <Borders> <Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/> <Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/> </Borders> </Style> </Styles> <Worksheet ss:Name="Nouvelle feuille"> <Table ss:ExpandedColumnCount="16384" ss:ExpandedRowCount="1" x:FullRows="1" ss:DefaultRowHeight="15"> <Column ss:Width="86.25"/> <Column ss:Width="96"/> <Column ss:Width="54.75"/> <Column ss:Width="51.75"/> <Column ss:Width="53.25"/> <Column ss:Width="69.75"/> <Column ss:Width="27.75"/> <Column ss:Width="56.25"/> <Column ss:Width="58.5"/> <Column ss:Width="23.25"/> <Column ss:Width="163.5"/> <Column ss:Index="13" ss:Width="77.25"/> <Row> <Cell ss:StyleID="s113"><Data ss:Type="Number">1</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">10</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">101</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">1</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">010</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">10</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">B</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="Number">1</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="Number">101</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="Number">103</Data></Cell> <Cell ss:StyleID="s113"><Data ss:Type="String">test</Data></Cell> <Cell ss:Index="13" ss:StyleID="s113"><Data ss:Type="String">13</Data></Cell> </Row> </Table> </Worksheet> </Workbook>
我希望通过该XML字符串创建XSSFWorkbook实例,以便轻松处理其中的数据,请问这是否可行?
回答
直接用这个XML创建XSSFWorkbook不可行。原因是你拿到的XML属于Excel的SpreadsheetML格式(旧版Office XML规范,对应Excel 2003及更早版本),而XSSFWorkbook是Apache POI专门用来处理OOXML格式(即.xlsx,Excel 2007+的开放XML格式)的类,两者的文件结构完全不兼容。
不过有两种可行的处理方案:
方案1:直接解析SpreadsheetML XML(推荐)
既然已经拿到了XML字符串,直接用Java的XML解析工具提取数据更高效,不需要额外格式转换。可以定位到XML中的<Row>和<Cell>节点,提取数据后直接转换成SpreadsheetView需要的格式:
// 假设xmlStr是剪贴板获取到的XML字符串 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new InputSource(new StringReader(xmlStr))); // 处理命名空间,避免找不到节点 XPath xpath = XPathFactory.newInstance().newXPath(); xpath.setNamespaceContext(new NamespaceContext() { @Override public String getNamespaceURI(String prefix) { if ("ss".equals(prefix)) { return "urn:schemas-microsoft-com:office:spreadsheet"; } return NamespaceContext.DEFAULT_NS_PREFIX; } @Override public String getPrefix(String namespaceURI) { return null; } @Override public Iterator<?> getPrefixes(String namespaceURI) { return null; } }); // 提取所有行数据 NodeList rowNodes = (NodeList) xpath.evaluate("//ss:Row", doc, XPathConstants.NODESET); ObservableList<ObservableList<Object>> data = FXCollections.observableArrayList(); for (int i = 0; i < rowNodes.getLength(); i++) { Node rowNode = rowNodes.item(i); NodeList cellNodes = (NodeList) xpath.evaluate("./ss:Cell", rowNode, XPathConstants.NODESET); ObservableList<Object> rowData = FXCollections.observableArrayList(); for (int j = 0; j < cellNodes.getLength(); j++) { Node cellNode = cellNodes.item(j); String dataType = xpath.evaluate("./ss:Data/@ss:Type", cellNode); String value = xpath.evaluate("./ss:Data/text()", cellNode); // 根据数据类型转换值 if ("Number".equals(dataType)) { rowData.add(Double.parseDouble(value)); } else { rowData.add(value); } } data.add(rowData); } // 将数据设置到SpreadsheetView spreadsheetView.setItems(data);
方案2:转换为XSSFWorkbook支持的格式
如果一定要用XSSFWorkbook处理,可以先把SpreadsheetML转换成.xlsx格式:
- 将XML字符串写入临时文件(后缀设为
.xml) - 用Apache POI的
WorkbookFactory读取该文件,会自动返回对应旧版格式的HSSFWorkbook - 把
HSSFWorkbook的数据复制到新创建的XSSFWorkbook中
示例代码:
// 写入临时XML文件 File tempXml = File.createTempFile("excel_clipboard", ".xml"); try (FileWriter writer = new FileWriter(tempXml)) { writer.write(xmlStr); } // 读取旧版Excel格式文件 Workbook hssfWorkbook = WorkbookFactory.create(tempXml); // 转换为XSSFWorkbook XSSFWorkbook xssfWorkbook = new XSSFWorkbook(); Sheet srcSheet = hssfWorkbook.getSheetAt(0); Sheet destSheet = xssfWorkbook.createSheet(); // 复制行数据 for (int rowNum = 0; rowNum <= srcSheet.getLastRowNum(); rowNum++) { Row srcRow = srcSheet.getRow(rowNum); Row destRow = destSheet.createRow(rowNum); if (srcRow != null) { for (int cellNum = 0; cellNum < srcRow.getLastCellNum(); cellNum++) { Cell srcCell = srcRow.getCell(cellNum); if (srcCell != null) { Cell destCell = destRow.createCell(cellNum); // 根据单元格类型处理值转换 switch (srcCell.getCellType()) { case NUMERIC: destCell.setCellValue(srcCell.getNumericCellValue()); break; case STRING: destCell.setCellValue(srcCell.getStringCellValue()); break; default: destCell.setCellValue(srcCell.toString()); } } } } } // 后续可使用XSSFWorkbook处理数据 // ... // 清理临时文件 tempXml.deleteOnExit();
注意:方案2需要额外的格式转换步骤,性能不如方案1。如果只是为了给SpreadsheetView填充数据,优先选方案1。
内容的提问来源于stack exchange,提问作者Luca170598
相关产品推荐
相关产品推荐

