2009-07-15 13 views

risposta

17

Questo è dettagliato nel user guide. Il download JAXB da http://jaxb.java.net/ include un esempio di come analizzare un blocco alla volta.

Quando un documento è di grandi dimensioni, è solito perché c'è ripetitivo parti in esso. Forse è un acquisto ordine con un ampio elenco di elementi pubblicitari, o forse si tratta di un file di registro XML con numero elevato di voci di registro.

Questo tipo di XML è adatto per l'elaborazione di blocchi ; l'idea principale è di utilizzare l'API StAX, eseguire un ciclo e singoli pezzi unmarshal separatamente. Il tuo programma agisce su un singolo pezzo e poi lo getta via. In questo modo manterrai solo il numero di in memoria, il che consente a di elaborare documenti di grandi dimensioni.

Vedi l' esempio di streaming-unmarshalling e la esempio parziale unmarshalling nella distribuzione JAXB RI per di più su come fare questo. L'esempio streaming unmarshalling ha un vantaggio di poter gestire blocchi a livello di nido arbitrario, ma richiede ad affrontare con il modello push --- JAXB unmarshaller sarà "push" nuova pezzo a voi e voi' Avrai bisogno di processarli subito lì.

Al contrario, il esempio parziale deserializzazione lavora in un modello pull (che solito rende il trattamento più semplice), ma questo approccio ha alcune limitazioni in porzioni associazione dati diversi dal ripetuti parte.

+0

Sì, questo è uno dei siti che ho trovato durante la ricerca di questo, ma non sono riuscito a trovare gli esempi di "streaming-unmarshalling" e "partial-unmarshalling" a cui si fa riferimento nella sezione 4.4.1. –

+7

Dispari. Dove stai guardando? Ho appena scaricato il JAR da jaxb.dev.java.net/2.1.12, decompresso, e lì sotto "samples" è "partial-unmarshalling" e "stream-unmarshalling". – skaffman

24

Poiché il codice è importante, ecco un PartialUnmarshaller che legge un grosso file in blocchi. Può essere utilizzato in questo modo new PartialUnmarshaller<YourClass>(stream, YourClass.class)

import javax.xml.bind.JAXBContext; 
import javax.xml.bind.JAXBException; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.stream.*; 
import java.io.InputStream; 
import java.util.List; 
import java.util.NoSuchElementException; 
import java.util.stream.Collectors; 
import java.util.stream.IntStream; 

import static javax.xml.stream.XMLStreamConstants.*; 

public class PartialUnmarshaller<T> { 
    XMLStreamReader reader; 
    Class<T> clazz; 
    Unmarshaller unmarshaller; 

    public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException { 
     this.clazz = clazz; 
     this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller(); 
     this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream); 

     /* ignore headers */ 
     skipElements(START_DOCUMENT, DTD); 
     /* ignore root element */ 
     reader.nextTag(); 
     /* if there's no tag, ignore root element's end */ 
     skipElements(END_ELEMENT); 
    } 

    public T next() throws XMLStreamException, JAXBException { 
     if (!hasNext()) 
      throw new NoSuchElementException(); 

     T value = unmarshaller.unmarshal(reader, clazz).getValue(); 

     skipElements(CHARACTERS, END_ELEMENT); 
     return value; 
    } 

    public boolean hasNext() throws XMLStreamException { 
     return reader.hasNext(); 
    } 

    public void close() throws XMLStreamException { 
     reader.close(); 
    } 

    void skipElements(int... elements) throws XMLStreamException { 
     int eventType = reader.getEventType(); 

     List<Integer> types = asList(elements); 
     while (types.contains(eventType)) 
      eventType = reader.next(); 
    } 
} 
+0

Ho bisogno di utilizzare XMLStreamConstants.START_DOCUMENT e così via affinché funzioni. –

+0

@MatthiasWuttke è possibile aggiungerli come importazione statica. importazione statica javax.xml.stream.XMLStreamConstants. *; –

+1

Potrebbe anche essere necessario Ints.asList di Guava o in java8 IntStream.of (elementi) .boxed(). Collect (Collectors.toList()); –

2

risposta di Yves Amsellem è piuttosto buona, ma funziona solo se tutti gli elementi sono esattamente dello stesso tipo. Altrimenti l'unmarshall genererà un'eccezione, ma il lettore avrà già consumato i byte, quindi non sarebbe possibile ripristinarli. Invece, dovremmo seguire il consiglio di Skaffman e guardare l'esempio dal barattolo JAXB.

Per spiegare come funziona:

  1. Creare un unmarshaller JAXB.
  2. Aggiungi un listener allo Unmarshaller per intercettare gli elementi appropriati. Questo viene fatto "hacking" da ArrayList per garantire che gli elementi non vengano archiviati in memoria dopo essere stati eliminati.
  3. Creare un parser SAX. Questo è dove avviene lo streaming.
  4. Utilizzare unmarshaller per generare un gestore per il parser SAX.
  5. Stream!

Ho modificato la soluzione in modo generico *. Tuttavia, ha richiesto un po 'di riflessione. Se questo non è OK, si prega di guardare gli esempi di codice nei barattoli JAXB.

ArrayListAddInterceptor.java

import java.lang.reflect.Field; 
import java.util.ArrayList; 

public class ArrayListAddInterceptor<T> extends ArrayList<T> { 
    private static final long serialVersionUID = 1L; 

    private AddInterceptor<T> interceptor; 

    public ArrayListAddInterceptor(AddInterceptor<T> interceptor) { 
     this.interceptor = interceptor; 
    } 

    @Override 
    public boolean add(T t) { 
     interceptor.intercept(t); 
     return false; 
    } 

    public static interface AddInterceptor<T> { 
     public void intercept(T t); 
    } 

    public static void apply(AddInterceptor<?> interceptor, Object o, String property) { 
     try { 
      Field field = o.getClass().getDeclaredField(property); 
      field.setAccessible(true); 
      field.set(o, new ArrayListAddInterceptor(interceptor)); 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 

} 

Main.java

public class Main { 
    public void parsePurchaseOrders(AddInterceptor<PurchaseOrder> interceptor, List<File> files) { 
     try { 
      // create JAXBContext for the primer.xsd 
      JAXBContext context = JAXBContext.newInstance("primer"); 

      Unmarshaller unmarshaller = context.createUnmarshaller(); 

      // install the callback on all PurchaseOrders instances 
      unmarshaller.setListener(new Unmarshaller.Listener() { 
       public void beforeUnmarshal(Object target, Object parent) { 
        if (target instanceof PurchaseOrders) { 
         ArrayListAddInterceptor.apply(interceptor, target, "purchaseOrder"); 
        } 
       } 
      }); 

      // create a new XML parser 
      SAXParserFactory factory = SAXParserFactory.newInstance(); 
      factory.setNamespaceAware(true); 
      XMLReader reader = factory.newSAXParser().getXMLReader(); 
      reader.setContentHandler(unmarshaller.getUnmarshallerHandler()); 

      for (File file : files) { 
       reader.parse(new InputSource(new FileInputStream(file))); 
      } 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

* Questo codice non è stato testato ed è solo a scopo illustrativo.

Problemi correlati