首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java SAX解析

Java SAX解析
EN

Stack Overflow用户
提问于 2011-12-22 17:42:47
回答 5查看 11.3K关注 0票数 15

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

我做我需要的没有问题,但我的代码变得相当复杂,我确信没有理由这样做,我可以用不同的方式做事情。我的XML的结构是这样的:

代码语言:javascript
复制
<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的实例。在解析时,我保留了告诉我是否在teams部分的布尔标志,以便在endElement中知道名称是球队的名称,而不是球员的名称,依此类推。

下面是我的代码:

代码语言:javascript
复制
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;
        }
    }
}

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

我应该采取不同的做法吗?例如,我如何知道一个名字标签属于什么?

谢谢!

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2011-12-22 20:08:14

在编写SAX解析器时有一个巧妙的技巧:允许它在解析时更改XMLReader的ContentHandler。这允许将不同元素的解析逻辑分离到多个类中,从而使解析更加模块化和可重用。当一个处理程序看到它的end元素时,它会切换回它的父元素。你要实现多少个处理程序就留给你了。代码将如下所示:

代码语言:javascript
复制
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);
        }
    }
}
票数 23
EN

Stack Overflow用户

发布于 2011-12-23 01:51:59

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

票数 3
EN

Stack Overflow用户

发布于 2011-12-22 18:05:11

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8602001

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档