2010-02-16 3 views
4

J'ai cherché sur le Web et j'ai cherché stackoverflow de haut en bas. Pas de solution. Bien que j'ai trouvé des solutions comment faire cela dans le pur xslt here.Fusion efficace de plusieurs fichiers xml de grande taille en un

Mais le problème est que le fichier XML résultant aura une taille de plusieurs centaines de Mo. Je dois donc le faire avec SAX en Java. (S'il vous plaît pas de solution xslt, bien que je l'ai marqué avec xslt ;-))

Laissez-moi expliquer avec plus de détails. J'ai plusieurs fichiers xml multiples (InputSteam préférables) qui devraient être analysés. Les fichiers ou les regards de InputStream comme

inputstream1

<root> 
    <doc> 
    <tag>test1</tag> 
    </doc> 
    <doc> 
    <tag>test2</tag> 
    </doc> 
    ... 
</root> 

inputstream2

<root> 
    <doc> 
    <tag>test3</tag> 
    </doc> 
    <doc> 
    <tag>test4</tag> 
    </doc> 
    ... 
</root> 

inputstream1 + inputstream2 + ... + inputstreamN = résultant xml. Cela ressemblera à

<root> 
    <doc> 
    <tag>test1</tag> 
    </doc> 
    <doc> 
    <tag>test2</tag> 
    </doc> 
    ... 
    <doc> 
    <tag>test3</tag> 
    </doc> 
    <doc> 
    <tag>test4</tag> 
    </doc> 
    ... 
</root> 

Est-ce que quelqu'un a une solution ou un lien pour cela? Est-ce possible en implémentant un InputSource personnalisé ou dois-je utiliser un ContentHandler personnalisé? Ou est-ce possible avec joost/stx?

La bonne chose si je pouvais utiliser un ContentHandler serait que je pourrais appliquer quelques transformations mineures (j'ai déjà implémenté cela). Mais le problème est que je ne sais pas un moyen de transmettre plusieurs fichiers ou des années InputStream comme InputSource:

XMLReader xmlReader = XMLReaderFactory.createXMLReader(); 
xmlReader.setContentHandler(customHandler); 
xmlReader.parse(getInputSource()); // only one InputStream possible 

ou devrais-je analyser les InputStreams directement dans mon ContentHandler?

Répondre

0

j'ai finalement réussi ce via l'extrait suivant:

finalHandler = new StreamResult(new OutputStreamWriter(System.out)); 
    // customHandler extends DefaultHandler 
    CustomTransformerHandler customHandler = new CustomTransformerHandler(
     finalHandler); 
    customHandler.startDocumentExplicitly(); 
    InputStream is = null; 
    while ((is = customHandler.createNextInputStream()) != null) { 
    // multiple inputStream parsing 
    XMLReader myReader = XMLReaderFactory.createXMLReader(); 
    myReader.setContentHandler(customHandler); 
    myReader.parse(new InputSource(is)); 
    } 
    customHandler.endDocumentExplicitly(); 

L'important était de laisser les méthodes startDocument et EndDocument vide. Toutes les autres méthodes (caractères, startElement, endElement) seront redirigées vers le gestionnaire finalHandler. La méthode customHandler.createNextInputStream renvoie null si tous les flux d'entrée sont lus.

1

Vous voudrez peut-être jeter un oeil à la version payante de Saxon. Il peut gérer XSLT à la volée sans avoir besoin du DOM complet en mémoire.

+0

hmmh, dans xslt, vous pouvez rechercher le premier nœud et le dernier nœud, où que vous soyez. C'est à dire: tout doit être en mémoire ... par définition de xslt. Ou que penses-tu? – Karussell

+0

Il existe un assez grand sous-ensemble de programmes XSLT dont vous n'avez pas besoin pour exécuter les arbres DOM complets en mémoire. –

+0

ah, ok merci. Maintenant je comprends – Karussell

2

Je ne l'ai pas fait moi-même, mais je me suis souvenu avoir vu un article d'IBM developerworks qui semblait avoir rendu cela plutôt facile.

Il est un peu vieux maintenant, mais essayez http://www.ibm.com/developerworks/xml/library/x-tipstx5/index.html

Ceci est StAX au lieu de SAX. Je ne suis pas sûr que les JDK actuels incluent StAX. Sinon, vous pouvez probablement obtenir de http://stax.codehaus.org/

+0

merci pour le lien. Je vais étudier cela! – Karussell

+0

+1 JDK inclut StAX depuis 1.5 pour autant que je m'en souvienne. Beaucoup plus pratique à utiliser que SAX. – helpermethod

0

La méthode la plus efficace pour fusionner des fichiers consiste à utiliser la fonctionnalité Couper et Coller au niveau des octets proposée par VTD-XML, AFAIK. Vous prenez les deux fichiers, les analysez dans des objets VTDNav, puis instanciez un objet XMLModifier, récupérez les fragments du second fichier et les insérez dans le premier fichier ... qui doit être beaucoup plus efficace que SAX .. Également le XML résultant obtient une instruction écrite sur un fichier - il n'est pas nécessaire de le stocker en mémoire. Voici le code complet en moins de 20 lignes ...

import com.ximpleware.*; 
import java.io.*; 

public class merge { 
    // merge second.xml into first.xml assuming the same encoding 
    public static void main(String[] s) throws VTDException, IOException{ 
     VTDGen vg = new VTDGen(); 
     if (!vg.parseFile("d:\\xml\\first.xml", false)) 
      return; 
     VTDNav vn1=vg.getNav(); 
     if(!vg.parseFile("d:\\xml\\second.xml", false)) 
      return; 
     VTDNav vn2 = vg.getNav(); 
     XMLModifier xm = new XMLModifier(vn1); 
     long l = vn2.getContentFragment(); 
     xm.insertBeforeTail(vn2, l); 
     xm.output("d:\\xml\\merged.xml"); 
    } 
} 
+0

hmmh, mais je ne veux pas les avoir en mémoire ... il suffit de les diriger directement sur le disque. Et je ne comprends pas comment cela sera plus rapide que le saxophone. – Karussell

+0

en utilisant sax vous faites beaucoup plus que simplement les rediriger vers le disque, beaucoup de frais généraux d'analyse de SAX est un gaspillage complet de cycles, en utilisant VTD-XML je ne serai pas surprenant de voir une perforation 10x (au moins) amélioration ... –

+0

ok. merci pour l'indice vtd-xml. Cela semble prometteur (d'après ce que je peux lire sur le site web de sourceforge). Mais même si cela peut être 100 fois plus rapide. Si cela prend 100% de RAM de la doc (ou même plus) je ne peux pas l'utiliser: il se pourrait que le xml résultant ne rentre même pas dans la mémoire. – Karussell

Questions connexes