Java SAX解析

我需要解析一个XML流。 因为我只需要做一次并构建我的java对象,所以SAX看起来是自然的选择。 我正在扩展DefaultHandler并实现startElement,endElement和characters方法,在我的类中有成员保存当前读取值(在characters方法中)。

我做我需要的东西没有问题,但我的代码变得非常复杂,我确信没有理由这样做,我可以做不同的事情。 我的XML结构是这样的:

         2009 9  true      2007 11   2009 7      

当我意识到在文件的几个区域中使用相同的标记名称时,我的问题就出现了。 例如,玩家和团队都存在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 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(); } 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; } } } 

因为在我的真实场景中,除了团队之外我还有更多的节点,并且这些节点也有像name和id这样的标签,我发现自己弄乱了几个类似于inTeamsSection的布尔值,而且我的endElement方法变得冗长而且复杂很多条件。

我该怎么办? 我怎么知道名称标签属于什么?

谢谢!

编写SAX解析器时有一个巧妙的技巧:允许在解析时更改XMLReader的ContentHandler 。 这允许将不同元素的解析逻辑分成多个类,这使得解析更加模块化和可重用。 当一个处理程序看到它的结束元素时,它会切换回其父元素。 您实施的处理程序数量将由您自行承担。 代码如下所示:

 public class RootHandler extends DefaultHandler { private XMLReader reader; private List teams; public RootHandler(XMLReader reader) { this.reader = reader; this.teams = new LinkedList(); } 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); } } } 

如果不了解更多关于你的要求的话,很难提出建议,但是你对“我的代码变得非常复杂”感到惊讶的事实表明,当你选择SAX时,你并没有充分了解情况。 SAX是一个具有非常高性能的低级编程接口,但这是因为解析器为您做的工作少得多,因此您需要自己做更多的工作。

我强烈建议您停止解析自己,并获取良好的XML数据绑定库。 XStream( http://x-stream.github.io/ )可能是个人喜爱的,但有许多不同的库。 它甚至可以在现场解析您的POJO,无需任何配置(如果您使用属性名称和复数来匹配XML结构)。

我做了一些非常相似的事情,但是我没有使用boolean标志来告诉我我处于什么状态,而是测试playerteam是非null 。 使事情有点整洁。 这需要您在将每个元素添加到相关列表后检测到每个元素的结尾时将它们设置为null

如果您需要更漂亮的代码请使用StAX, 所有XML解析API的比较表明StAX是一个更好的选择。

大多数测试中的StAX性能也优于任何其他API实现。

所以我个人认为没有任何理由继续使用SAX,除非你正在做一些遗留相关的编程。