Java 解析损坏的XML
我试图解析一些第三方XML,它有许多我认为是“非法”的特性Java 解析损坏的XML,java,xml,Java,Xml,我试图解析一些第三方XML,它有许多我认为是“非法”的特性 多个根元素 “匿名”关闭标记 包含-- 开始和结束标记不匹配 范例 <foo> <toto>123</> <!-- == "anonymous" close tag --> <tata> <titi>456</> </> <!-- == "anonymous"
- 多个根元素
- “匿名”关闭标记
- 包含--
- 开始和结束标记不匹配
<foo>
<toto>123</> <!-- == "anonymous" close tag -->
<tata>
<titi>456</>
</> <!-- == "anonymous" close tag-->
</foo>
<bar> <!-- == multiple root elements -->
</bar>
123
456
这是我从未听说过的XML的变体吗?到目前为止,我所发现的一切,包括,都表明这不是XML
标记名区分大小写;开始标记和结束标记必须完全匹配
单个根元素包含所有其他元素
我只是想知道在Java中用什么最简单的方法来解析它而不必求助于正则表达式。我正在考虑将初始解析更正为XML,这样我就可以使用XPath或其他标准机制。我发现解决这一问题的唯一方法是对整个输入进行标记化,然后逐段重新构建,过滤掉问题,正确关闭标记等 此外,我还修复了多个根标记的问题,用新元素root包装整个内容,即
${content}
公共类BrokenXmlParser{
公共字符串分析(InputStream资源)引发IOException{
StringBuilder=新的StringBuilder();
try(BufferedReader=new BufferedReader(new InputStreamReader(资源))){
字符串行=null;
而((line=reader.readLine())!=null){
builder.append(第+行“\n”);
}
}
List tokens=tokenize(builder.toString());
返回正确的(代币);
}
私有字符串正确(列出令牌){
StringBuilder重新组装=新建StringBuilder();
重新组装。追加(“”);
Deque tagNameStack=new ArrayDeque();
布尔跳跃=假;
for(inti=0;i 如果(“我发现解决这个问题的唯一方法是将整个输入标记化,然后逐个重建,过滤掉问题,正确地关闭标记等等
此外,我还修复了多个根标记的问题,用新元素root包装整个内容,即${content}
公共类BrokenXmlParser{
公共字符串分析(InputStream资源)引发IOException{
StringBuilder=新的StringBuilder();
try(BufferedReader=new BufferedReader(new InputStreamReader(资源))){
字符串行=null;
而((line=reader.readLine())!=null){
builder.append(第+行“\n”);
}
}
List tokens=tokenize(builder.toString());
返回正确的(代币);
}
私有字符串正确(列出令牌){
StringBuilder重新组装=新建StringBuilder();
重新组装。追加(“”);
Deque tagNameStack=new ArrayDeque();
布尔跳跃=假;
for(inti=0;i 如果(”乍一看,我想说的是,您通常需要使用解析器来处理此问题。最简单的解决方案可能是返回源代码并请求更干净的XML数据。我的建议是,不要使用正则表达式。坏消息是,除非有可用的文档,否则您可能需要找出并构建自己的解析器对于这个奇怪的习惯用法,请注意。匿名关闭标记是一个问题-你确定每个开始标记都有一个吗?否则,就没有办法解析它。@RealDis疑者是的,总是匹配关闭标记,尽管同一文件中的一些标记正确关闭。例如,在1997-8年开发XML期间,有一个建议允许最小化d关闭标记,如….该语法已被Microsoft XML parser的beta版接受,但在1998年批准1.0标准时,他们很快取消了对该语法的支持。SGML中允许使用该语法,因此您可能会发现您可以使用SGML语法分析器来解析此文档。乍一看,您需要使用语法分析器来处理此pro总的来说是一个问题。最简单的解决方案可能是返回源代码并请求更干净的XML数据。我的建议是,不要使用正则表达式。坏消息是,您可能需要根据您拥有的文档找出并构建自己的解析器,除非有一个解析器可用于这种奇怪的习惯用法。匿名关闭标记是一个问题-您确定re对于每个开始标记正好是一个?否则,就没有办法解析它。@realpoint Yes始终匹配关闭标记,尽管同一文件中的某些标记已正确关闭。例如,在1997-8年开发XML期间,有一项建议允许最小化关闭标记,如……该语法已被Microsoft X的测试版接受ML解析器,但在1998年批准1.0标准时,他们很快就取消了对它的支持。它在SGML中是允许的,因此您可能会发现可以使用SGML解析器解析此文档。
public class BrokenXmlParser {
public String parse(InputStream resource) throws IOException {
StringBuilder builder = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource))) {
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line + "\n");
}
}
List<String> tokens = tokenize(builder.toString());
return correct(tokens);
}
private String correct(List<String> tokens) {
StringBuilder reassemble = new StringBuilder();
reassemble.append("<root>");
Deque<String> tagNameStack = new ArrayDeque<>();
boolean skip = false;
for (int i = 0; i < tokens.size(); i++) {
String token = tokens.get(i);
if ("<".equals(token)) {
tagNameStack.push(tokens.get(i + 1));
}
if ("<?".equals(token) || "<!".equals(token)) {
// skip comments
skip = true;
} else if ("<".equals(token)) {
skip = false;
}
if ("</>".equals(token)) {
reassemble.append("</" + tagNameStack.pop() + ">");
} else if ("</".equals(token)) {
// sometimes tags are incorrectly closed
reassemble.append("</" + tagNameStack.pop() + ">");
i = i + 2;
} else if (!skip) {
reassemble.append(token);
}
}
reassemble.append("</root>");
return reassemble.toString();
}
private List<String> tokenize(String input) {
List<String> tokens = new ArrayList<>();
Pattern tag = Pattern.compile("(</>|<!|<\\?|\\?>|</|<|>|\\s+|[^<> ]+)");
Matcher matcher = tag.matcher(input);
while (matcher.find()) {
tokens.add(matcher.group(1));
}
return tokens;
}
}