我总是发现XML有点麻烦处理。我不是在谈论实现XML解析器:我是在谈论使用现有的基于流的解析器,例如SAX解析器,该解析器逐节点处理XML。
是的,为这些解析器学习各种API确实很容易,但是每当我查看处理XML的代码时,我总是发现它有些复杂。根本的问题似乎是XML文档在逻辑上被分离为各个节点,但是数据类型和属性通常与实际数据分离,有时通过多层嵌套。因此,当单独处理任何特定节点时,需要维护许多额外的状态以确定我们在哪里以及下一步需要做什么。
例如,给出一个典型XML文档的片段:
<book>
<title>Blah blah</title>
<author>Blah blah</author>
<price>15 USD</price>
</book>
...如何确定何时遇到包含书名的文本节点?假设我们有一个简单的XML解析器,它就像一个迭代器,每次调用时,都会为我们提供XML文档中的下一个节点XMLParser.getNextNode()
。我不可避免地发现自己正在编写如下代码:
boolean insideBookNode = false;
boolean insideTitleNode = false;
while (!XMLParser.finished())
{
....
XMLNode n = XMLParser.getNextNode();
if (n.type() == XMLTextNode)
{
if (insideBookNode && insideTitleNode)
{
// We have a book title, so do something with it
}
}
else
{
if (n.type() == XMLStartTag)
{
if (n.name().equals("book")) insideBookNode = true
else if (n.name().equals("title")) insideTitleNode = true;
}
else if (n.type() == XMLEndTag)
{
if (n.name().equals("book")) insideBookNode = false;
else if (n.name().equals("title")) insideTitleNode = false;
}
}
}
基本上,XML处理很快就会变成一个巨大的,由状态机驱动的循环,其中包含许多状态变量,用于指示我们之前发现的父节点。否则,需要维护堆栈对象以跟踪所有嵌套标签。这很快变得容易出错并且难以维护。
同样,问题似乎是我们感兴趣的数据没有直接与单个节点关联。当然,如果我们将XML编写为:
<book title="Blah blah" author="blah blah" price="15 USD" />
...但是实际上很少使用XML。通常,我们将文本节点作为父节点的子节点,并且需要跟踪父节点以确定文本节点指的是什么。
所以...我做错什么了吗?有没有更好的办法?在什么时候使用基于XML流的解析器变得太麻烦了,以至于需要成熟的DOM解析器?我想听听其他程序员在使用基于流的解析器处理XML时会使用哪种惯用法。基于流的XML解析必须总是变成一个巨大的状态机吗?