JAXB peut analyser de gros fichiers XML en morceaux

je dois analyser des fichiers XML potentiellement volumineux, dont le schéma m'est déjà fourni dans plusieurs fichiers XSD, de sorte que la reliure XML est fortement favorisée. Je voudrais savoir si je peux utiliser JAXB pour analyser le fichier en morceaux et si oui, comment.

21
demandé sur John F. 2009-07-16 01:26:25

3 réponses

parce que le code Compte, voici un PartialUnmarshaller qui lit un gros fichier en morceaux. Il peut être utilisé de cette façon 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();
    }
}
25
répondu yves amsellem 2015-05-29 14:46:04

Ceci est détaillé dans le guide de l'utilisateur. Le téléchargement de JAXB de http://jaxb.java.net/ comprend un exemple de comment analyser un morceau à la fois.

quand un document est grand, il est habituellement, car il est répétitif les pièces en elle. C'est peut-être un achat commande avec une grande liste d'articles, ou peut-être que c'est un fichier de log XML avec grand nombre d'entrées de journal.

ce TYPE DE XML est adapté pour traitement de morceaux; l'idée principale est d' utilisez L'API StAX, lancez une boucle, et morceaux individuels non marshaux séparément. Votre programme agit sur un seul morceau, et qu'on jette ensuite. De cette façon, vous serez en ne gardant à plus un morceau en mémoire, ce qui permet vous traiter de grands documents.

voir le streaming-unmarshalling exemple et le démontage partiel exemple dans la distribution JAXB RI pour en savoir plus sur la façon de faire. Le exemple de streaming-unmarshalling avantage qu'il peut traiter des morceaux à arbitraire nid, mais il nécessite vous de traiter avec le modèle push --- JAXB unmarshaller va "push" nouveau morceau de vous et vous aurez besoin de processus de leur droit.

en revanche, le désembuage partiel exemple fonctionne dans un modèle d'extraction (qui rend généralement le traitement plus facile), mais cette approche présente certaines limites dans les parties de la Banque de données autres que la répéter en partie.

18
répondu skaffman 2018-04-04 12:56:27

la réponse D'Yves Amsellem est assez bonne, mais ne fonctionne que si tous les éléments sont du même type. Sinon votre unmarshall lancera une exception, mais le lecteur aura déjà consommé les octets, donc vous ne pourrez pas récupérer. Au lieu de cela, nous devrions suivre les conseils de Skaffman et regarder l'échantillon de la jarre JAXB.

Pour expliquer comment il fonctionne:

  1. créer un JAXB unmarshaller.
  2. Ajouter un écouteur à l'unmarshaller pour intercepter les éléments appropriés. Pour ce faire, il faut" Hacker " l'ArrayList pour s'assurer que les éléments ne sont pas stockés en mémoire après avoir été démontés.
  3. créer un analyseur de SAX. C'est ici que le streaming a lieu.
  4. utilisez l'unmarshaller pour générer un handler pour l'analyseur de SAX.
  5. Stream!

j'ai modifié la solution générique*. Cependant, elle nécessite une certaine réflexion. Si ce N'est pas correct, veuillez regarder les échantillons de code dans le JAXB pot.

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);
        }
    }
}
2
répondu James Watkins 2015-10-10 17:58:25