Java SAX 解析

标签 java xml sax

我需要解析一个 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 的实例。解析时,我保留了 boolean 标志,告诉我我是否在团队部分,以便在 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 的 boolean 值,我的 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);
        }
    }
}

关于Java SAX 解析,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8602001/

相关文章:

ruby - 巨型 xml 文档的 open-uri 和 sax 解析

java - Android:BluetoothSocket 接收自己的输出

c# - 单个xpath表达式获取一些属性

java - 使用 Sax 解析大型 XML 文件

java - 针对泛型和特定类型或该类型列表的灵活 JAXB XML 解析

java - XQuery 表达式 - 屏幕抓取 - Saxon/JTidy

java - 如何使用 SAX 正确解析 XML?

java - 无法更改 java.awt.MenuBar 中的字体

java - 使用 iText 将标题添加到 pdf

java - AsyncTask 不工作 - 无法实例化类型错误