Java SAX Парсинг - PullRequest
       5

Java SAX Парсинг

14 голосов
/ 22 декабря 2011

Есть поток XML, который мне нужно проанализировать.Так как мне нужно сделать это только один раз и собрать свои Java-объекты, SAX выглядит естественным выбором.Я расширяю DefaultHandler и реализую методы startElement, endElement и 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>

Моя проблема началась, когда я понял, что одни и те же имена тегов используются в нескольких областях файла.Например, идентификатор и имя существуют как для игрока, так и для команды.Я хочу создать экземпляры моих классов Java Player и Team.Во время синтаксического анализа я держал логические флаги, указывающие, нахожусь ли я в разделе команд, чтобы в конце элемента я знал, что имя - это имя команды, а не имя игрока и т. Д.

Вот как мойкод выглядит так:

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 становится длинным и сложным со многими условиями.

Что я должен делать по-другому?Как я могу узнать, например, к какому тегу относится имя?

Спасибо!

Ответы [ 5 ]

23 голосов
/ 22 декабря 2011

При написании синтаксического анализатора SAX есть одна хитрость: разрешено изменять ContentHandler XMLReader при синтаксическом анализе.Это позволяет разделить логику синтаксического анализа для различных элементов на несколько классов, что делает синтаксический анализ более модульным и многократно используемым.Когда один обработчик видит свой конечный элемент, он переключается обратно на своего родителя.Сколько обработчиков вы реализуете, будет оставлено вам.Код будет выглядеть так:

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);
        }
    }
}
2 голосов
/ 22 декабря 2011

Трудно дать совет, не зная больше о ваших требованиях, но тот факт, что вы удивлены тем, что «мой код стал довольно сложным», говорит о том, что вы не были в курсе, когда выбрали SAX. SAX - это низкоуровневый интерфейс программирования, способный к очень высокой производительности, но это потому, что синтаксический анализатор выполняет гораздо меньше работы для вас, и поэтому вам нужно выполнять гораздо больше работы самостоятельно.

1 голос
/ 22 декабря 2011

Я настоятельно рекомендую прекратить синтаксический анализ самостоятельно и взять хорошую библиотеку XML-привязки данных. XStream (http://x -stream.github.io / ) может быть личным фаворитом, но есть много разных библиотек. Он может даже анализировать ваши POJO на месте, без какой-либо настройки (если вы используете имена свойств и множественное число для соответствия структуре XML).

0 голосов
/ 22 декабря 2011

Если вам нужен более красивый код, пожалуйста, используйте StAX, это сравнение всех API-интерфейсов синтаксического анализа XML предполагает, что StAX - гораздо лучший вариант.

StAX производительность в большинстве тестов лучше, чем в любой другой реализации API.

Так что я лично не вижу причин продолжать работу с SAX, если вы не занимаетесь каким-либо унаследованным программированием.

0 голосов
/ 22 декабря 2011

Я делаю что-то очень похожее, но вместо флагов boolean, чтобы сообщить мне, в каком я состоянии, я проверяю, не является ли player или team не-1004 *. Делает вещи немного аккуратнее. Это требует, чтобы вы установили их на null при обнаружении конца каждого элемента после добавления его в соответствующий список.

...