[英]Java SAX Parsing
我需要解析一个XML流。 由于我只需要做一次就可以构建我的java对象,因此SAX看起来很自然。 我正在扩展DefaultHandler并实现startElement,endElement和character方法,在我的类中具有保存当前读取值(在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解析器时有一个巧妙的窍门:允许在解析时更改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);
}
}
}
在不了解您的要求的情况下很难提出建议,但是您对“我的代码变得非常复杂”感到惊讶,这表明您在选择SAX时没有足够的信息。 SAX是具有很高性能的低级编程接口,但这是因为解析器为您所做的工作要少得多,因此您需要自己做很多工作。
我强烈建议您停止分析自己,并获取良好的XML数据绑定库。 XStream( http://x-stream.github.io/ )可能是个人最喜欢的,但是有许多不同的库。 它甚至可以现场解析您的POJO,而无需任何配置(如果您使用属性名称和复数形式来匹配XML结构)。
我做的事情非常相似,但是我没有测试boolean
来告诉我我处于什么状态,而是测试player
或team
是否为非null
。 使事情变得更整洁。 这要求您在将每个元素的末尾添加到相关列表之后,将它们设置为null
。
如果您需要更漂亮的代码,请使用StAX, 对所有XML解析API的比较表明StAX是更好的选择。
大多数测试中的StAX性能也优于任何其他API实现。
因此,除非您正在做一些与遗留相关的编程,否则我个人认为继续使用SAX没有任何理由。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.