2011-05-10 29 views
9

Estoy usando JAXB para analizar un archivo XML en mi aplicación basada en GWT. El XML es el siguiente (un ejemplo simplificado):Análisis filtrado de JAXB

<addressbook> 

    <company name="abc"> 
     <contact> 
      <name>...</name> 
      <address>...</address> 
     </contact> 

     <contact> 
      <name>...</name> 
      <address>...</address> 
     </contact> 

     <contact> 
      <name>...</name> 
      <address>...</address> 
     </contact> 
     ... 
     ... 
    </company> 

    <company name="def"> 
     <contact> 
      <name>...</name> 
      <address>...</address> 
     </contact> 
     ... 
     ... 
    </company> 

    ... 
    ... 

</addressbook> 

he definido las clases, como se muestra a continuación:

@XmlRootElement(name="addressbook") 
public class Addressbook implements Serializable { 

    private ArrayList<Company> companyList = new ArrayList<Company>(); 

    public Addressbook() {    
    } 

    @XmlElement(name = "company") 
    public ArrayList<Company> getCompanyList() { 
     return companyList; 
    } 


} 

============================= 

@XmlRootElement(name="company") 
public class Company implements Serializable { 

    private String name; 

    private ArrayList<Contact> contactList = new ArrayList<Contact>(); 

    public Company() {  
    } 

    @XmlAttribute 
    public String getName() { 
     return name; 
    } 

    @XmlElement(name = "contact") 
    public ArrayList<Contact> getContactList() { 
     return contactList; 
    } 

    ... 
    ... 
} 

============================= 

@XmlRootElement(name="contact") 
public class Contact implements Serializable 
{ 
    private String name; 
    private String address; 

    public Contact() { 
    } 

    @XmlElement 
    public String getName() 
    { 
     return name; 
    } 

    @XmlElement 
    public String getAddress() 
    { 
     return address; 
    } 

    ... 
    ... 
} 

Este es el código:

try { 
    JAXBContext jc = JAXBContext.newInstance(Addressbook.class); 
    Unmarshaller um = jc.createUnmarshaller(); 
    addressbook = (Addressbook) um.unmarshal(new FileReader("ds/addressbook.xml"));   
} catch (JAXBException e) { 
    e.printStackTrace(); 
} 

Necesito obtener la lista de contactos según el nombre de la empresa. Por ejemplo, obtenga todos los contactos para la compañía "abc". Puedo analizar todo el archivo XML y luego filtrar manualmente los registros. Pero si el archivo de entrada es grande, podría ser más eficiente analizar solo lo que necesito. Entonces, ¿es posible especificar un criterio por adelantado y analizar solo registros específicos?

Gracias.

Respuesta

1

Usted podría

  • Aplicar una transformación XSLT en el archivo XML, o
  • Resolver referencia el archivo en un DOM, y utilizar XPath para seleccionar los nodos que desea

antes de pasar los objetos resultantes del método unmarsal

Sin embargo, podría ser más simple crear una memoria en Map con la siguiente clave:

public class SearchableAddressBook { 

    public final Map<String, Company> companyMap = new HashMap<String,Company>(); 

    public SearchableAddressBook(List<Company> companyList) { 
     for (Company company: companyList) { 
      companyMap.add(company.getName(), company)); 
     } 

} 

O cree un DB en memoria si realmente desea sobre-diseñarlo.

+0

gracias por tu respuesta. ¿Podría mostrar (o señalarme) el código de muestra usando el más simple de estos enfoques? Lo siento, todavía soy nuevo en esto. – DFB

+0

Actualicé mi respuesta, aunque el enfoque de Mapa aún analiza todo el archivo XML, por lo que podría no ser lo que está buscando. ¡Recuerde medir el rendimiento en una variedad de conjuntos de datos! – artbristol

+0

¿Está sugiriendo que al modificar mi clase de la libreta de direcciones, la descalificación obtendría los datos en un mapa (que sería genial, en realidad)?¿O debería crear una nueva clase SearchableAddressBook para convertir la lista en un mapa "after" unmarshalling? Gracias. – DFB

10

Se podría utilizar la extensión @XmlPath en EclipseLink JAXB (MOXy) para manejar este caso (soy el plomo tecnología moxy):

@XmlRootElement(name="addressbook") 
public class Addressbook implements Serializable { 

    private ArrayList<Company> companyList = new ArrayList<Company>(); 

    public Addressbook() {    
    } 

    @XmlPath("company[@name='abc']") 
    public ArrayList<Company> getCompanyList() { 
     return companyList; 
    } 


} 

Para más información:


ACTUALIZACIÓN - Uso StreamFilter

El siguiente ejemplo demuestra cómo un StreamFilter podrían ser aprovechados para este caso de uso:

import java.io.FileInputStream; 

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLStreamReader; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(Addressbook.class); 

     XMLInputFactory xif = XMLInputFactory.newFactory(); 
     FileInputStream xmlStream = new FileInputStream("input.xml"); 
     XMLStreamReader xsr = xif.createXMLStreamReader(xmlStream); 
     xsr = xif.createFilteredReader(xsr, new CompanyFilter()); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     Addressbook addressbook = (Addressbook) unmarshaller.unmarshal(xsr); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(addressbook, System.out); 
    } 
} 

La aplicación de la StreamFilter es el siguiente:

import javax.xml.stream.StreamFilter; 
import javax.xml.stream.XMLStreamReader; 

public class CompanyFilter implements StreamFilter { 

    private boolean accept = true; 

    public boolean accept(XMLStreamReader reader) { 
     if(reader.isStartElement() && "company".equals(reader.getLocalName())) { 
      accept = "abc".equals(reader.getAttributeValue(null, "name")); 
     } else if(reader.isEndElement()) { 
      boolean returnValue = accept; 
      accept = true; 
      return returnValue; 
     } 
     return accept; 
    } 

} 
+0

Coincidentemente, estaba leyendo tu blog en busca de ideas cuando publicabas tu respuesta. Creo que esto es lo que estoy buscando, pero realmente preferiría evitar bibliotecas adicionales para esto si es posible. De lo contrario, consideraré usar MOXy. En otra nota, en lugar de desasociar los objetos de la compañía en una lista, ¿puedo deshacerme de ellos como un mapa? – DFB

+0

@DFB: puede separar los objetos de la empresa de un Mapa (http://bdoughan.blogspot.com/2010/09/processing-atom-feeds-with-jaxb.html). Si es posible utilizar StreamFilter para obtener el comportamiento que desea (http://download.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html#createFilteredReader(javax.xml.stream) .XMLStreamReader,% 20javax.xml.stream.StreamFilter), usando las API de JAXB estándar. –

+0

gracias por su respuesta. Buscaré en StreamFilter. En cuanto a desasignar en un mapa, tengo que admitir que no fui capaz de entender Salga de su blog. Creo que tengo mucho más que aprender incluso antes de comenzar a publicar preguntas :) – DFB