解析破碎的XML

时间:2016-09-28 09:58:00

标签: java xml

我试图解析一些第三方XML,其中有许多我认为是非法的"特征

  • 多个根元素
  • "匿名"关闭标签
  • 评论包含 -
  • 不匹配的开始和结束标记

实施例

<foo>
  <toto>123</>  <!-- == "anonymous" close tag -->
  <tata>
     <titi>456</>
  </>     <!-- == "anonymous" close tag-->
</foo>
<bar>   <!-- == multiple root elements -->
</bar>

这是XML的一些变种吗?我没有听说过?到目前为止,我发现的所有内容(包括Well-formedness and error-handling)都表明这不是XML。

  

标记名称区分大小写; start-tag和end-tag必须完全匹配。

     

单个根元素包含所有其他元素。

我只是想知道在Java中解析这个问题的最简单方法,而不必诉诸正则表达式。我正在考虑使用初始解析来更正XML,以便我可以使用XPath或其他标准机制。

1 个答案:

答案 0 :(得分:0)

我找到解决这个问题的唯一方法是将整个输入标记化并逐个过滤掉问题,正确关闭标记等。

此外,我通过使用新元素root包装整个内容来修复多个根标签问题,即<root>${content}</root>

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;

    }

}