2011-05-10 5 views
9

J'utilise JAXB pour analyser un fichier XML dans mon application basée sur GWT. Le XML ressemble à ceci (un exemple simplifié):Analyse syntaxique 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> 

J'ai défini les classes comme indiqué ci-dessous:

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

    ... 
    ... 
} 

Voici le code:

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

Je dois obtenir la liste des contacts en fonction du nom de l'entreprise. Par exemple, obtenez tous les contacts pour la société "abc". Je peux analyser le fichier XML entier, puis filtrer manuellement les enregistrements. Mais si le fichier d'entrée est grand, il peut être plus efficace d'analyser uniquement ce dont j'ai besoin. Est-il donc possible de spécifier un critère à l'avance et d'analyser uniquement des enregistrements spécifiques?

Merci.

Répondre

1

Vous pouvez soit

  • Appliquer une transformation XSLT dans le fichier XML ou
  • unmarshall le fichier dans un DOM, et utiliser XPath pour sélectionner les nœuds que vous souhaitez

avant de passer le ou les objets résultants à la méthode unmarshal

Il peut être plus simple, cependant, de créer une mémoire Map avec le nom de l'entreprise:Vous pouvez également créer une base de données en mémoire si vous souhaitez vraiment la sur-ingénierie.

+0

remerciements pour votre réponse. Pourriez-vous montrer (ou me diriger vers) un exemple de code en utilisant la plus simple de ces approches? Désolé, je suis encore nouveau dans ce domaine. – DFB

+0

Mise à jour de ma réponse, bien que l'approche Map analyse toujours l'ensemble du fichier XML, ce n'est peut-être pas ce que vous cherchez. N'oubliez pas de mesurer la performance sur une variété de jeux de données! – artbristol

+0

Suggérez-vous qu'en modifiant ma classe Addressbook, l'unmarshalling obtiendrait les données dans une carte (ce serait génial, en fait)?Ou devrais-je créer une nouvelle classe SearchableAddressBook pour convertir la liste en une carte "après" unmarshalling? Merci. – DFB

10

Vous pouvez utiliser l'extension @XmlPath dans EclipseLink JAXB (MOXy) pour traiter ce cas (je suis le chef de file de la technologie 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; 
    } 


} 

Pour plus d'informations:


MISE À JOUR - L'utilisation StreamFilter

L'exemple ci-dessous montre comment un StreamFilter pourrait être mise à profit pour ce cas d'utilisation:

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 mise en œuvre de la StreamFilter se présente comme suit:

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

Par coïncidence, je lisais votre blog pour des idées lorsque vous postez votre réponse. Je pense que c'est ce que je cherche, mais je préférerais vraiment éviter les bibliothèques supplémentaires pour cela si c'est possible. Sinon, je vais envisager d'utiliser MOXy. Sur une autre note, au lieu d'unmarshalling les objets de la compagnie dans une liste, puis-je les démasquer comme une carte? – DFB

+0

@DFB - Vous pouvez démélanger les objets de la société à une carte (http://bdoughan.blogspot.com/2010/09/processing-atom-feeds-with-jaxb.html). S'il peut être possible d'utiliser un StreamFilter pour obtenir le comportement souhaité (http://download.oracle.com/javase/6/docs/api/javax/xml/stream/XMLInputFactory.html#createFilteredReader(javax.xml.stream .XMLStreamReader,% 20javax.xml.stream.StreamFilter), en utilisant les APIs JAXB standard –

+0

merci pour votre réponse.Je vais regarder dans StreamFilter.En ce qui concerne unmarshalling dans une carte, je dois admettre que je ne pouvais pas comprendre Je suppose que j'ai encore beaucoup à apprendre avant même de commencer à poster des questions :) – DFB