2011-12-22 5 views
13

Существует поток XML, который мне нужно проанализировать. Поскольку мне нужно только сделать это один раз и построить мои объекты Java, SAX выглядит как естественный выбор. Я расширяю DefaultHandler и реализую методы startElement, endElement и characters, имея членов в моем классе, где я сохраняю текущее значение чтения (взятое в методе символов).Java SAX Parsing

У меня нет проблем делать то, что мне нужно, но мой код довольно сложный, и я уверен, что для этого нет причин, и я могу делать что-то по-другому. Структура моего 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. Во время разбора я сохранил логические флаги, говорящие мне, есть ли я в разделе команд, чтобы в конце этого элемента я узнал, что имя - это имя команды, а не имя игрока и т. Д.

Вот как мой код выглядит следующим образом:

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; 
     } 
    } 
} 

Поскольку в моем реальном сценарии я больше узлов на игрока в дополнение к командам, и эти узлы имеют метки, как имя и идентификатор, я обнаружил, перепутались с несколькими булерами, подобными inTeamsSection, и мой метод endElement становится длинным и сложным со многими условиями.

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

Спасибо!

+1

Я бы сказал, что использование SAX было естественным выбором около 7 лет назад. В настоящее время естественным выбором является использование JAXB (или Xtream, или XmlBeans или JibX). – Tarlog

+0

Иногда вам просто нужно вручную разобрать парсинг. Когда вы имеете дело с мегабайтами XML-преобразования его в объекты Java, это не очень хорошая идея. –

+0

@ ʘleg - Если вы используете JAXB для демонтажа объектов из StAX XMLStreamReader, вы можете отключить подразделы большего документа для управления ограничениями памяти. –

ответ

20

При написании синтаксического анализатора 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); 
     } 
    } 
} 
+0

, если есть Subteams, Players и т. Д., Не все ли они должны содержать ссылку друг на друга, что приведет к жесткой связи _VERY_? –

+1

Каждый обработчик должен знать о своем родительском обработчике и о возможных дочерних обработчиках, поэтому определенно существует некоторая связь. Но, например, обработчик для 'start-date' не должен знать о обработчике для' player'. –

+0

Спасибо, теперь я использую эту серию, и она отлично работает для меня. Только то, что мне нужно для этого прецедента. – Haji

1

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

0

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

0

Если вам нужен более красивый код, пожалуйста, используйте StAX, этот comparison of all XML parsing APIs предполагает, что StAX - намного лучший вариант.

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

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

2

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

Смежные вопросы