Java读取Excel类型XML文档时XPath查询失败的解决求助
Java读取Excel格式XML并导入数据库的解决方案
问题根源
你踩的坑主要是俩:一是XML带命名空间但你没处理,直接用无命名空间的XPath根本找不到节点;二是代码里getElementsByTagName("")传空参数,这肯定拿不到任何元素,而且你写的XPath路径/Worksheet/Data和实际XML的节点结构不匹配。
修正方案
1. 先搞定命名空间
Excel导出的XML(SpreadsheetML)都带默认命名空间urn:schemas-microsoft-com:office:spreadsheet,用DOM和XPath时必须先注册这个命名空间,不然解析器认不出节点。
2. 写对XPath路径
目标XML的实际数据节点层级是Workbook -> Worksheet -> Table -> Row -> Cell -> Data,得用带命名空间前缀的XPath才能定位。
3. 调整后的可运行代码
下面是改好的代码,能直接读取目标XML的数据,后续替换成JDBC代码就能存数据库:
import org.w3c.dom.*; import javax.xml.parsers.*; import javax.xml.xpath.*; import java.io.*; import java.net.URL; import java.util.Iterator; public class ExcelXmlReader { public static void main(String[] args) { try { // 从指定URL加载XML(也可替换成本地文件路径) URL xmlUrl = new URL("https://www.juntadeandalucia.es/datosabiertos/portal/dataset/91f3c95b-a2d2-4828-8880-99f8ea156d0e/resource/f7737217-65d6-4b00-bd10-2f82c69ae2f7/download/contratos-adjudicados-oct-23.xml"); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); // 必须开启命名空间支持,否则无法识别带命名空间的节点 factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(xmlUrl.openStream()); doc.getDocumentElement().normalize(); XPath xPath = XPathFactory.newInstance().newXPath(); // 注册命名空间前缀,用"ss"对应Excel的默认命名空间 xPath.setNamespaceContext(new NamespaceContext() { @Override public String getNamespaceURI(String prefix) { if ("ss".equals(prefix)) { return "urn:schemas-microsoft-com:office:spreadsheet"; } return null; } @Override public String getPrefix(String namespaceURI) { return null; } @Override public Iterator getPrefixes(String namespaceURI) { return null; } }); // 正确的XPath:定位所有数据行 String xpathExpr = "/ss:Workbook/ss:Worksheet/ss:Table/ss:Row"; NodeList rowNodes = (NodeList) xPath.compile(xpathExpr).evaluate(doc, XPathConstants.NODESET); // 遍历每一行,读取单元格数据 for (int i = 0; i < rowNodes.getLength(); i++) { Node rowNode = rowNodes.item(i); if (rowNode.getNodeType() == Node.ELEMENT_NODE) { Element rowElement = (Element) rowNode; // 按命名空间获取单元格节点 NodeList cellNodes = rowElement.getElementsByTagNameNS("urn:schemas-microsoft-com:office:spreadsheet", "Cell"); StringBuilder rowData = new StringBuilder(); for (int j = 0; j < cellNodes.getLength(); j++) { Element cellElement = (Element) cellNodes.item(j); // 获取单元格内的Data节点文本内容 NodeList dataNodes = cellElement.getElementsByTagNameNS("urn:schemas-microsoft-com:office:spreadsheet", "Data"); if (dataNodes.getLength() > 0) { rowData.append(dataNodes.item(0).getTextContent()).append("\t"); } } // 此处替换为JDBC代码即可将数据存入数据库 System.out.println(rowData.toString().trim()); } } } catch (Exception e) { e.printStackTrace(); } } }
额外提醒
- 存数据库别手写原生JDBC,用MyBatis或Spring JdbcTemplate,能省掉很多连接管理、异常处理的麻烦。
- 如果XML文件很大,别用DOM解析,改用SAX解析,内存占用更低,SAX处理时同样要通过命名空间URI判断节点。
- 先把XML下载到本地,用Notepad++或Chrome开发者工具查看节点结构,确认层级和命名空间后再写解析逻辑,比瞎试效率高。
内容的提问来源于stack exchange,提问作者FRENEK WHAT
相关产品推荐
相关产品推荐

