在没有使用Jackson,JDK和Document Builder实现的情况下,是否有一种在Java中创建原始XML解析器的好方法?理论上这应该是什么样的?
例如,实现基本元素定义语法的解析:
<root>
<first></first>
<second></second>
</root>
实现解析元素树的漂亮打印:每个元素在一个单独的上 line,嵌套元素缩进4个空格并基本转义&#39;&lt;&#39;和&#39;&gt;&#39;符号通过&lt;和&gt;什么时候 解析:
<escaped<>></escaped<>>
然后,实现解析内联空元素:
<root>
<empty/>
</root>
答案 0 :(得分:1)
我使用基于编译正则表达式的标记化器构建递归下降解析器(虽然使用正则表达式解析 XML是不可能的,使用XML的正则表达式没有任何问题标记化)。
我大致如下设计了tokenizer接口(在内部使用Scanner):
class XmlTokenizer {
String getCurrentText();
boolean tryConsumeStartTagStart();
boolean tryConsumeAttributeName();
boolean tryConsumeText();
...
然后,解析器可以尝试在当前位置有效的令牌类型,并使用getCurrentText()方法获取相应的文本(例如元素或属性名称)。
例如,内容解析看起来如下:
void parseNodeContent(Node node) {
while (true) {
if (tokenizer.tryConsumeText()) {
node.appendText(getCurrentText());
} else if (tryConsumeStartTagStart()) {
node.append(parseElement());
} else if
... // processing instructions, entity references
} else {
// Let the caller deal with all the rest, including errors.
break;
}
}
}
元素解析可能如下所示:
// Precondition: on element start
// Postcondition: on element end
Element parseElement() {
Element result = new Element(tokenizer.getCurrentText());
while (tryConsumeAttributeName()) {
String attributeName = tokenizer.getCurrentText();
... // attribute value parsing
}
if (!tokenizer.tryConsumeStartTagEndSelfClosing()) {
if (!tokenizer.tryConsumeStartTagEnd()) {
throw new RuntimeException("Invalid start tag end");
}
parseContent(result);
if (!tokenizer.tryConsumeEndTag(result.name())) {
throw new RuntimeException("End tag missing for " +result.name());
}
}
return result;
}
我从Kenton Varla的协议缓冲区定义解析器中选择了tryConsumeXxx()
样式,我发现它非常有用。
答案 1 :(得分:0)
第一步很清楚你需要标记化XML。在该步骤之后,您将获得令牌列表 - 例如标记,属性,值等。
下一步取决于实施。如果您提到DOM,则必须处理整个列表。所以'&lt;'打开标签创建,然后名称来,在可能的参数列表'&gt;'之后。然后必须将子注释构建到该特定节点的子树中。请注意,您需要更换像&amp;;进入&amp ;.
对于基本处理应该没问题。其他主题包括命名空间和验证......
答案 2 :(得分:0)
当然,可能有十几种不同的方法可以做到这一点。例如,XML适用于递归下降解析。在任何级别检查,XML文件都具有基本结构
<tag1>something</tag1><tag2>something</tag2>...
其中“something”本身与整个文件的结构相同。 “某事”可能是空的,标签本身可能会折叠成<tag/>
- 必须查找这些案例。
解析器查找<tag>something</tag>
形式的模式列表,如果“某些东西”以另一个<tag>
开头,它会递归调用自己来解析这个新节。当文档的所有部分都被探索到底部时,递归完成。在每个新的递归调用中,我们创建一个类的实例来保存数据,并且构建这些实例的递归树,而不是像DOM模型。
熟练的解析器设计师能够使整个事情更有效率,当然,你必须处理解析命名空间,属性等。但是使用递归解析器实现这样的东西是一个有趣的运动。