2011-12-22 12 views
13

Hay una secuencia XML que debo analizar. Como solo necesito hacerlo una vez y construir mis objetos java, SAX parece ser la elección natural. Extiendo DefaultHandler e implementando los métodos startElement, endElement y characters, teniendo miembros en mi clase donde guardo el valor de lectura actual (tomado en el método de caracteres).Java SAX Parsing

No tengo problemas para hacer lo que necesito, pero mi código se volvió bastante complejo y estoy seguro de que no hay razón para eso y que puedo hacer las cosas de manera diferente. La estructura de mi XML es algo como esto:

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

Mi problema empezó cuando me di cuenta de que los mismos nombres de las etiquetas se utilizan en varias áreas del archivo. Por ejemplo, la identificación y el nombre existen tanto para un jugador como para un equipo. Quiero crear instancias de mis clases Java Player y Team. Durante el análisis, mantuve las banderas booleanas para decirme si estoy en la sección de equipos para que al final sepa que el nombre es del equipo, no del jugador, etc.

Así es como mi código es el siguiente:

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

Dado que en mi escenario real que tengo más nodos a un jugador, además de los equipos y los nodos también tienen etiquetas como nombre e ID, me encontré en mal estado con varios booleanos similares a inTeamsSection y mi método endElement se vuelve largo y complejo con muchas condiciones.

¿Qué debo hacer de manera diferente? ¿Cómo puedo saber a qué etiqueta pertenece, por ejemplo?

Gracias!

+1

Yo diría que usar SAX fue una elección natural hace unos 7 años. Actualmente, la elección natural es usar JAXB (o Xtream, o XmlBeans o JibX) – Tarlog

+0

. Algunas veces solo tiene que hacer el análisis sintáctico manualmente. Cuando maneja megabytes de XML convirtiéndolo en objetos Java no es una buena idea. –

+0

@ ʘleg - Si usa JAXB para desasignar objetos de StAX XMLStreamReader, puede desglosar las subsecciones de un documento más grande para administrar las restricciones de memoria. –

Respuesta

20

Hay un truco genial al escribir un analizador SAX: Se permite cambiar el ContentHandler de un XMLReader durante el análisis. Esto permite separar la lógica de análisis para diferentes elementos en múltiples clases, lo que hace que el análisis sea más modular y reutilizable. Cuando un manejador ve su elemento final, vuelve a su elemento primario. Cuantos manejadores usted implemente se quedarán en usted.El código se vería así:

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

si hay Subteams, Reproductores, etc., ¿no deberían todos ellos contener referencias entre ellos, lo que daría como resultado un _VERY_ acoplamiento ajustado? –

+1

Cada controlador debería saber sobre su controlador principal y los posibles manejadores secundarios, por lo que definitivamente hay algún acoplamiento. Pero, por ejemplo, el manejador de 'start-date' no necesitará saber sobre el manejador de' player'. –

+0

Gracias, ahora estoy usando este truco y funciona muy bien para mí. Justo lo que necesitaba para este caso de uso. – Haji

1

Recomiendo encarecidamente dejar de analizarse usted mismo, y tomar una buena biblioteca de enlace de datos XML. XStream (http://x-stream.github.io/) es mi favorito, pero hay muchas bibliotecas diferentes. Puede incluso analizar sus POJO en el momento, sin ninguna configuración requerida (si usa nombres de propiedad y pluralización para que coincida con la estructura XML).

0

hago algo muy similar, pero en lugar de tener boolean banderas decirme qué estado en que estoy, I para comprobar las player o team siendo no null. Hace las cosas un poco más ordenadas. Esto requiere que los configure en null cuando detecta el final de cada elemento, después de haberlo agregado a la lista correspondiente.

0

Si necesita un código más bonito, use StAX, este comparison of all XML parsing APIs sugiere que StAX es una opción mucho mejor.

StAX performance en la mayoría de las pruebas es mejor que el de cualquier otra implementación de API también.

Así que personalmente no veo ninguna razón para continuar con SAX a menos que esté haciendo alguna programación relacionada con legado.

2

Es difícil aconsejar sin saber más acerca de sus necesidades, pero el hecho de que usted está sorprendido de que "mi código de bastante compleja" sugiere que no estaban bien informados cuando eligió SAXÓFONO. SAX es una interfaz de programación de bajo nivel capaz de ofrecer un rendimiento muy alto, pero eso se debe a que el analizador le está haciendo mucho menos trabajo y, por lo tanto, necesita hacer mucho más trabajo por su cuenta.