Java SAX 解析
Posted
技术标签:
【中文标题】Java SAX 解析【英文标题】:Java SAX Parsing 【发布时间】:2012-01-25 23:36:53 【问题描述】:我需要解析一个 XML 流。由于我只需要执行一次并构建我的 java 对象,因此 SAX 看起来是自然的选择。我正在扩展 DefaultHandler 并实现 startElement、endElement 和 characters 方法,在我的类中有成员,我保存当前读取值(在 characters 方法中获取)。
我做我需要的事情没有问题,但我的代码变得相当复杂,我确信没有理由这样做,我可以做不同的事情。 我的 XML 的结构是这样的:
<players>
<player>
<id></id>
<name></name>
<teams total="2">
<team>
<id></id>
<name></name>
<start-date>
<year>2009</year>
<month>9</month>
</start-date>
<is-current>true</is-current>
</team>
<team>
<id></id>
<name></name>
<start-date>
<year>2007</year>
<month>11</month>
</start-date>
<end-date>
<year>2009</year>
<month>7</month>
</end-date>
</team>
</teams>
</player>
</players>
当我意识到文件的多个区域使用了相同的标签名称时,我的问题就开始了。例如,球员和球队都存在 id 和 name。我想创建我的 java 类 Player 和 Team 的实例。在解析时,我保留了布尔标志,告诉我我是否在团队部分,以便在 endElement 中我会知道该名称是团队的名称,而不是玩家的名称等等。
我的代码如下所示:
public class MyParser extends DefaultHandler
private String currentValue;
private boolean inTeamsSection = false;
private Player player;
private Team team;
private List<Team> teams;
public void characters(char[] ch, int start, int length) throws SAXException
currentValue = new String(ch, start, length);
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
if(name.equals("player"))
player = new Player();
if (name.equals("teams"))
inTeamsSection = true;
teams = new ArrayList<Team>();
if (name.equals("team"))
team = new Team();
public void endElement(String uri, String localName, String name) throws SAXException
if (name.equals("id"))
if(inTeamsSection)
team.setId(currentValue);
else
player.setId(currentValue);
if (name.equals("name"))
if(inTeamsSection)
team.setName(currentValue);
else
player.setName(currentValue);
if (name.equals("team"))
teams.add(team);
if (name.equals("teams"))
player.setTeams(teams);
inTeamsSection = false;
因为在我的真实场景中,除了球队之外,我还有更多的节点给玩家,并且这些节点也有名称和 ID 等标签,我发现自己搞砸了几个类似于 inTeamsSection 的布尔值,并且我的 endElement 方法变得很长并且复杂的条件很多。
我应该做些什么不同的事情?例如,我如何知道名称标签属于什么?
谢谢!
【问题讨论】:
我会说使用 SAX 是大约 7 年前的自然选择。目前自然的选择是使用 JAXB(或 Xtream,或 XmlBeans 或 JibX) 有时您只需要手动进行解析。当您处理兆字节的 XML 时,将其转换为 Java 对象并不是一个好主意。 @ʘleg - 如果您使用 JAXB 从 StAX XMLStreamReader 解组对象,您可以解组较大文档的子部分以管理内存限制。 'subsections' 表示子树,还是 XML 的一部分?如果有像编写 SAX 解析器时有一个巧妙的技巧:允许更改
解析时 XMLReader 的ContentHandler
。这允许分离
将不同元素的逻辑解析为多个类,这使得
解析更加模块化和可重用。当一个处理程序看到它的结束元素时
切换回其父级。您实现多少个处理程序将留给
你。代码如下所示:
public class RootHandler extends DefaultHandler
private XMLReader reader;
private List<Team> teams;
public RootHandler(XMLReader reader)
this.reader = reader;
this.teams = new LinkedList<Team>();
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
if (name.equals("team"))
// Switch handler to parse the team element
reader.setContentHandler(new TeamHandler(reader, this));
public class TeamHandler extends DefaultHandler
private XMLReader reader;
private RootHandler parent;
private Team team;
private StringBuilder content;
public TeamHandler(XMLReader reader, RootHandler parent)
this.reader = reader;
this.parent = parent;
this.content = new StringBuilder();
this.team = new Team();
// characters can be called multiple times per element so aggregate the content in a StringBuilder
public void characters(char[] ch, int start, int length) throws SAXException
content.append(ch, start, length);
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
content.setLength(0);
public void endElement(String uri, String localName, String name) throws SAXException
if (name.equals("name"))
team.setName(content.toString());
else if (name.equals("team"))
parent.addTeam(team);
// Switch handler back to our parent
reader.setContentHandler(parent);
【讨论】:
如果有子团队、玩家等,他们不是都必须包含彼此的引用,这会导致非常紧密耦合吗? 每个处理程序都必须知道它的父处理程序和可能的子处理程序,所以肯定存在一些耦合。但是例如,start-date
的处理程序不需要知道player
的处理程序。
谢谢,我现在正在使用这个 Treak,它对我很有用。正是我在这个用例中所需要的。【参考方案2】:
如果不了解更多关于您的需求,很难提出建议,但是您对“我的代码变得相当复杂”感到惊讶这一事实表明您在选择 SAX 时并没有充分了解情况。 SAX 是一种低级编程接口,具有非常高的性能,但这是因为解析器为您做的工作要少得多,因此您需要自己做更多的工作。
【讨论】:
【参考方案3】:我做了一些非常相似的事情,但不是让boolean
标志告诉我我处于什么状态,而是测试player
或team
是否不是null
。让事情变得更整洁一些。这要求您在将每个元素添加到相关列表后,在检测到每个元素的结尾时将其设置为 null
。
【讨论】:
【参考方案4】:我强烈建议您停止解析自己,并使用好的 XML 数据绑定库。 XStream (http://x-stream.github.io/) 可能是个人最喜欢的,但有许多不同的库。它甚至可以在现场解析您的 POJO,而无需任何配置(如果您使用属性名称和复数形式来匹配 XML 结构)。
【讨论】:
【参考方案5】:如果您需要更漂亮的代码,请使用 StAX,comparison of all XML parsing APIs 表明 StAX 是一个更好的选择。
StAX performance 在大多数测试中也优于任何其他 API 实现。
所以我个人认为没有任何理由继续使用 SAX,除非您正在进行一些与遗留相关的编程。
【讨论】:
以上是关于Java SAX 解析的主要内容,如果未能解决你的问题,请参考以下文章
java webserver-xml--熟悉SAX解析流程-存储