如何让DOMParser无异常解析无单个根节点的XML内容
解决方案
问题根因
你拿到的是多根节点的XML片段,不符合标准XML规范(标准XML要求有且仅有一个顶层根节点),Xerces系列的DOMParser和标准SAX解析器默认都会按规范做校验,所以会抛出根节点后内容格式非法的错误。
方案1:字符串包裹临时根节点(最推荐)
直接对原始XML字符串做预处理,前后拼接一对临时根节点标签即可,解析完成后取根节点的子节点就是原始的所有目标节点,实现成本极低:
import com.sun.org.apache.xerces.internal.parsers.DOMParser; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.IOException; import java.io.StringReader; public class XmlParsingTest { @Test public void test() throws IOException, SAXException { final DOMParser parser = new DOMParser(); final String rawXml = "<LetsGoBrandon></LetsGoBrandon><LetsGoBrandon></LetsGoBrandon>"; // 新增:包裹临时根节点 final String wrappedXml = "<tempRoot>" + rawXml + "</tempRoot>"; parser.parse(new InputSource(new StringReader(wrappedXml))); // 解析完成后获取原始节点 Document doc = parser.getDocument(); NodeList targetNodes = doc.getDocumentElement().getChildNodes(); // 后续正常处理targetNodes即可 } }
这个方案的优势:
- 实现逻辑简单,无额外依赖
- 兼容性强,适配所有版本的Xerces解析器
- 业务改造成本极低,不影响后续节点处理逻辑
方案2:流拼接包裹根节点(适合大体积XML)
如果原始XML体积很大,字符串拼接会导致内存占用过高,可以用流拼接的方式避免生成大字符串:
import com.sun.org.apache.xerces.internal.parsers.DOMParser; import org.junit.Test; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.SequenceInputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; public class XmlParsingTest { @Test public void testWithLargeXml() throws IOException, SAXException { final DOMParser parser = new DOMParser(); final String rawXml = "<LetsGoBrandon></LetsGoBrandon><LetsGoBrandon></LetsGoBrandon>"; // 构造三个流按顺序拼接 InputStream rootStart = new ByteArrayInputStream("<tempRoot>".getBytes(StandardCharsets.UTF_8)); InputStream rawStream = new ByteArrayInputStream(rawXml.getBytes(StandardCharsets.UTF_8)); InputStream rootEnd = new ByteArrayInputStream("</tempRoot>".getBytes(StandardCharsets.UTF_8)); SequenceInputStream combinedStream = new SequenceInputStream(Collections.enumeration( Arrays.asList(rootStart, rawStream, rootEnd) )); parser.parse(new InputSource(combinedStream)); // 后续节点处理逻辑和方案1一致 } }
注意事项
不推荐使用解析器的XML片段解析扩展特性,不同版本的Xerces实现差异大,兼容性差,生产环境稳定性远不如包裹临时根节点的方案。
内容的提问来源于stack exchange,提问作者Glory to Russia
相关产品推荐
相关产品推荐

