如何从连续流读取含/不含XML声明的XML原始字符串并拆分?
解决方案:拆分连续流中带/不带XML声明的完整XML块
针对你遇到的兼容问题——无法处理无XML声明的连续XML流拆分,结合每个XML都有固定root节点的特性,我们可以直接构造正则匹配完整的XML块,而非依赖分隔符拆分,这样就能完美覆盖所有场景。
核心思路
每个目标XML的结构无非两种:
- 带XML声明:
<?xml...?> <root>...</root> - 无XML声明:
<root>...</root>
我们可以用一个正则直接匹配这两种结构,一次性捕获完整的XML字符串,无需拆分后拼接。
匹配正则表达式
(?:<\?xml\s+.*?\?>\s*)?<root>.*?</root>
对正则各部分的说明:
(?:<\?xml\s+.*?\?>\s*)?:可选的XML声明匹配。(?:...)是非捕获组(避免额外捕获无关内容),?表示该组可省略;\s+匹配声明内的空格,.*?非贪婪匹配声明内容,\s*匹配声明后的任意空白(换行、空格等)。<root>.*?</root>:匹配固定的root节点,.*?非贪婪匹配内部所有内容,确保遇到第一个</root>就停止,正好对应单个XML的结束。
修改后的代码实现
try (Socket socket = server.accept()) { try (InputStream in = socket.getInputStream()) { Scanner scanner = new Scanner(new InputStreamReader(in, "UTF-8")); // 替换<root>为你实际的固定根节点名称 String xmlCapturePattern = "(?:<\\?xml\\s+.*?\\?>\\s*)?<root>.*?</root>"; List<String> completeXmlList = new ArrayList<>(); String currentXml; // 循环捕获所有完整XML块,直到流结束 while ((currentXml = scanner.findWithinHorizon(xmlCapturePattern, 0)) != null) { // 可选:去除前后空白,根据你的业务需求调整 currentXml = currentXml.trim(); completeXmlList.add(currentXml); // 调试用,可删除 System.out.println("捕获到完整XML:\n" + currentXml); } } }
为什么这个方案更可靠?
- 完全兼容带/不带XML声明的场景,也支持混合输入流。
- 直接捕获完整XML字符串,无需像原方案那样处理分隔符丢失的问题。
findWithinHorizon(pattern, 0)会持续扫描输入流直到找到匹配或流结束,非常适合处理Socket这类连续输入场景。
测试你的混合场景
针对你给出的混合输入:
<?xml version="1.0" standalone="yes"?> <root> </root> <?xml version="1.0" standalone="yes"?> <root> </root> <root> </root>
代码会将其拆分为3个独立的完整XML字符串:
<?xml version="1.0" standalone="yes"?> <root> </root><?xml version="1.0" standalone="yes"?> <root> </root><root> </root>
注意事项
- 如果你的root节点内部可能出现
</root>字符串(比如文本或CDATA中),当前正则会提前终止匹配。这种情况下需要调整正则,排除CDATA和文本中的匹配,比如:
这个版本用(?:<\?xml\s+.*?\?>\s*)?<root>(?:(?!<\/root>).)*<\/root>(?!<\/root>).确保只匹配到真正的root结束标签。 - 务必将正则中的
<root>替换为你实际业务中的固定根节点名称。
内容的提问来源于stack exchange,提问作者CovaDax
相关产品推荐
相关产品推荐

