如何在Java中解析单文件内的多个XML文档
处理串联XML文档的Java解析方案
你遇到的是串联XML文档(多个独立XML拼接在一个文件中),标准DOM解析器(DocumentBuilder)要求输入是合法的单根XML,因此会在第二个XML声明处抛出SAXParseException。无需拆分文件的话,有以下几种直接解析的可行方案:
方案一:使用StAX解析器(推荐,灵活高效)
StAX是拉模式的XML解析器,允许手动控制解析流程,非常适合处理串联XML。核心思路是循环读取每个XML片段的文档事件,处理完一个片段后再处理下一个。
示例代码:
import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamReader; import java.io.FileInputStream; public class ConcatenatedXmlParser { public static void main(String[] args) throws Exception { XMLInputFactory factory = XMLInputFactory.newInstance(); XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("your-file.xml")); while (reader.hasNext()) { int event = reader.next(); // 遇到文档开始标记,处理当前XML片段 if (event == XMLStreamConstants.START_DOCUMENT) { processSingleXmlFragment(reader); } } reader.close(); } private static void processSingleXmlFragment(XMLStreamReader reader) throws Exception { System.out.println("Processing new XML fragment:"); while (reader.hasNext()) { int event = reader.next(); if (event == XMLStreamConstants.END_DOCUMENT) { // 当前片段解析完成,返回主循环处理下一个 break; } // 复用你原有的标签处理逻辑,示例为打印元素名 if (event == XMLStreamConstants.START_ELEMENT) { System.out.println("Element: " + reader.getLocalName()); // 可替换为你的XMLTagLookUp.visitChildNodes相关逻辑 } } } }
方案二:SAX解析器结合PushbackReader
SAX是流式解析,通过PushbackReader检测每个XML的起始标记,手动重置解析器状态来逐个处理片段。
示例代码:
import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import java.io.FileReader; import java.io.PushbackReader; public class SaxConcatenatedParser { public static void main(String[] args) throws Exception { PushbackReader pushbackReader = new PushbackReader(new FileReader("your-file.xml"), 100); XMLReader xmlReader = XMLReaderFactory.createXMLReader(); // 设置自定义ContentHandler,处理单个XML片段的业务逻辑 xmlReader.setContentHandler(new YourCustomContentHandler()); char[] buf = new char[100]; int readLen; while ((readLen = pushbackReader.read(buf)) != -1) { String content = new String(buf, 0, readLen); int xmlDeclIndex = content.indexOf("<?xml"); if (xmlDeclIndex != -1) { // 推回XML声明起始后的内容,从这里开始解析当前片段 pushbackReader.unread(content.substring(xmlDeclIndex).toCharArray()); try { xmlReader.parse(new InputSource(pushbackReader)); } catch (SAXException e) { // 忽略片段结束时的"prolog不允许内容"异常,其他异常正常抛出 if (!e.getMessage().contains("Content is not allowed in prolog")) { throw e; } } } else { pushbackReader.unread(buf, 0, readLen); } } pushbackReader.close(); } }
方案三:动态包装为合法单根XML
通过过滤输入流,去掉每个XML的声明,并在整个文件外层添加统一根节点,让文件变成合法XML后,再用DocumentBuilder解析。
示例代码:
import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.*; public class WrappedXmlParser { public static void main(String[] args) throws Exception { File inputFile = new File("your-file.xml"); // 包装输入流,动态添加根节点并过滤XML声明 InputStream wrappedStream = new FilterInputStream(new FileInputStream(inputFile)) { private boolean isFirstFragment = true; private boolean inXmlDecl = false; private final StringBuilder declBuffer = new StringBuilder(); @Override public int read() throws IOException { int c = super.read(); if (c == -1) { // 文件末尾添加根节点闭合标签 return '>'; } // 过滤XML声明 if (c == '<' && !inXmlDecl) { int nextChar = super.read(); if (nextChar == '?') { inXmlDecl = true; declBuffer.append('<').append((char)nextChar); while (true) { int d = super.read(); if (d == -1) break; declBuffer.append((char)d); if (d == '?' && super.read() == '>') { inXmlDecl = false; declBuffer.setLength(0); // 第一个片段前添加根节点起始标签 if (isFirstFragment) { isFirstFragment = false; return '<'; } // 跳过声明,返回下一个有效字符 return super.read(); } } } else { super.unread(nextChar); if (isFirstFragment) { isFirstFragment = false; return '<'; } return c; } } if (inXmlDecl) { declBuffer.append((char)c); if (declBuffer.toString().endsWith("?>")) { inXmlDecl = false; declBuffer.setLength(0); return super.read(); } return read(); } return c; } }; // 现在可以用DocumentBuilder正常解析 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document document = builder.parse(wrappedStream); Element root = document.getDocumentElement(); NodeList nList = root.getChildNodes(); XMLTagLookUp.visitChildNodes(nList); wrappedStream.close(); } }
内容的提问来源于stack exchange,提问作者Zachari
相关产品推荐
相关产品推荐

