Bonjour,
Ma question concerne le traitement de fichiers XML avec XSLT, de tres gros fichiers. Etant completement debutant (avant de commencer a toucher a xsl il y a environ 1 mois ou 2, je ne connaissais que le nom et le concept), il se peut que de fasse des erreurs de raisonnement, excusez moi par avance.
Ainsi, on m'avait confier la tache de tester divers processeurs XSLT, et au vu de mes benchmarks, il etait evident qu'il faudrait qu'on change notre approche par rapport au traitement de gros fichiers XML, a moins qu'on passe notre temps a rajouter de la RAM sur les serveurs ^^;
Bref, la chose est que je dois trouver donc une solution pour traiter des gros fichiers XML pour les transformer en TXT, CSV, HTML et PDF.
Je possede des feuilles XSLT qui marchent deja parfaitement sur de petits fichiers.
Apres m'etre apercu qu'il etait trop couteux en temps d'apprentissage et d'installation de l'environemement, j'ai ecarte la piste (qui avait helas l'air bien) des processeurs de flux bases sur des languages comme OCaml (XStream, etc.).
STX aussi avait l'air prometteur, mais helas 3 fois plus lent compare a ce que Xalan faisait deja avec nos feuilles XSL (non optimisees ?).
L'approche normale avec des processeurs XSLT etant impossible elle aussi, j'ai pense utiliser l'option special pour traiter en streaming avec Saxon.
Je dois dire que j'ai resolu le probleme avec la fonction la plus recente couplant <saxon:iterate> et saxon:stream().
Helas, on me rajoute une nouvelle condition : il faut que les feuilles XSL que je cree soient compatibles et traitables aussi avec n'importe quel autre processeur XSLT (sur des petits fichiers par exemple...). Abandon donc des fonctions saxon:iterate et saxon:stream, la seule possibilite qu'il me reste est de revenir a l'utilisation de l'attribut read-once="true" dans un element <xsl:copy>, comme explique ici : http://www.saxonica.com/documentatio...cs/serial.html
Vient enfin le probleme : pourquoi j'utilisais l'autre fonction ? Parce que j'ai besoin que mon processeur se rappelle de choses qu'il a lu avant pour traiter la suite.
Pour faire simple, voici le type de fichier XML que j'ai a traiter, et dont je n'ai pas le droit de changer le format :
A l'interieur de <logs></logs>, il peut y avoir des centaines de mega de donnees a travers des milliers d'element <log>.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <columnSet> <column>column3</column> <column>column1</column> <columnSet> <logs> <log> <column1>text 1</column1> <column2>text 2</column2> <column3>text 3</column3> </log> <log> <column1>text a</column1> <column2>text b</column2> <column3>text c</column3> </log> </logs>
En sortie, il faut sortir a chaque ligne le texte entre les balises <column*>, en fonction du contenu de <columnSet>. Dans le cas present, il faudrait avoir en sortie :
text 3 text 1
text c text a
Pour compliquer la chose, le code que j'ai ecrit est compris dans d'autres elements, et le columnSet change donc pour d'autres <logs>.
Avec Saxon, il faut catcher dans la commande <xsl:copy-of select="doc('source.xml')//(columnSet|log)" saxon:read-once="yes" xmlns:saxon="http://saxon.sf.net/"/> tous les elements que l'on veut traiter par la suite, et en plus il faut qu'il soit de petite taille (pour eviter le prob de OutOfmemoryError que je cherche a regler en utilisant un traitement par streaming).
Ainsi, aucun moyen de faire catcher le contenu de <logs> d'un coup, ou meme de l'element encadrant le code XML que j'ai ecris.
Bref, je pensais pouvoir faire qqchose avec a un certain moment dans ma feuille XSL :
Le code XML que j'ai a traiter est legerement plus complique que ce que j'ai decrit, mais en bref je n'arrive deja pas a faire ce que je veux avec la version simplifiee que j'ai explique.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 <xsl:template match="log"> <xsl:param name="columnSet" /> <xsl:variable name="log" select="."/> <tr> <xsl:for-each select="$columnSet"> <td nowrap="true" bgcolor="#FFFFFF" valign="top" style="font-size:8pt;"> <xsl:choose> <xsl:when test=".='column1'"> <xsl:value-of select="$log/column1"/> </xsl:when> <xsl:when test=".='column2'"> <xsl:value-of select="$log/column2"/> </xsl:when> <xsl:when test=".='column3'"> <xsl:value-of select="$log/column3"/> </xsl:when> <xsl:otherwise/> </xsl:choose> </td> </xsl:for-each> </tr> <xsl:apply-templates> <xsl:with-param name="tableOpened" select="$tableOpened" /> </xsl:apply-templates> </xsl:template>
J'ai par exemple des elements <name> et <remark> a traiter de facon differente selon les endroits ou ils se trouvent.
Par ex :
Ainsi, dans mon code HTML de sortie, le titre du rapport au debut est a traiter differemment des titres de sous rapports... et apparamment, si dans mon select associe a <xsl:copy-of> je cherche a catcher report/name et search/name, saxon ne semple pas pouvoir marcher en streaming pour ca...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 <report> <name>Rapport</name> <remark>Description</remark> <search> <name>Sous rapport 1</name> <remark>Description sous rapport 1</remark> <logs> <log>(...)</log> <log>(...)</log> </logs> </search> <search> <name>Sous rapport 1</name> <remark>Description sous rapport 1</remark> <logs> <log>(...)</log> <log>(...)</log> </logs> </search> </report>
Je pensais donc regler tout ceci avec un copy-of genre :
<xsl:copy-of select="doc('source.xml')//(name|remark|columnSet|log)" saxon:read-once="yes" xmlns:saxon="http://saxon.sf.net/"/>
Puis, a l'aide de plusieurs templates et param/with-param, je comptais passer de template en template mes variables, etc..
Voici un extrait de mon code :
Hors lorsque je l'execute, je m'apercois que mes variables ne sont pas du tout passees aux autres templates, a aucun moment. De meme, le code source XML ne semble pas se faire catcher correctement, et est rendu direct en copie dans la sortie par les templates par defaut.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 <xsl:function name="f:report_source"> <xsl:copy-of select="doc($document)//(date|time|name|remark|term|columnSet)" saxon:read-once="yes" xmlns:saxon="http://saxon.sf.net/"/> </xsl:function> <xsl:template name="main"> <html> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS"/> <body> <xsl:apply-templates select="f:report_source()" /> </body> </html> </xsl:template> <xsl:template match="date"> <xsl:apply-templates> <xsl:with-param name="reportDate" select="." /> </xsl:apply-templates> </xsl:template> <xsl:template match="time"> <xsl:param name="reportDate" /> <xsl:apply-templates> <xsl:with-param name="reportDate" select="$reportDate" /> <xsl:with-param name="reportTime" select="." /> <xsl:with-param name="headerProcessed" select="false()" /> </xsl:apply-templates> </xsl:template> <xsl:template match="name"> <xsl:param name="reportDate" /> <xsl:param name="reportTime" /> <xsl:param name="headerProcessed" /> <xsl:text>pouet</xsl:text> <xsl:choose> <xsl:when test="$headerProcessed"> <!-- /report/condition/search/name --> <xsl:apply-templates> <xsl:with-param name="headerProcessed" select="$headerProcessed" /> <xsl:with-param name="searchName" select="." /> </xsl:apply-templates> </xsl:when> <xsl:otherwise> <!-- /report/name --> <xsl:apply-templates> <xsl:with-param name="headerProcessed" select="$headerProcessed" /> <xsl:with-param name="reportDate" select="$reportDate" /> <xsl:with-param name="reportTime" select="$reportTime" /> <xsl:with-param name="reportName" select="." /> </xsl:apply-templates> </xsl:otherwise> </xsl:choose> </xsl:template>
Je n'ai pas l'impression de pouvoir utiliser <xsl:call-template> vu mon architecture, du coup je dois utiliser <xsl:apply-templates>, tout en passant mes parametres aux templates suivants. Le probleme semble neanmoins bien venir de la, mais je ne comprends pas pouvoir. J'ai beau lire et relire la specification de XSLT, je n'arrive pas a comprendre pourquoi mon code ne fonctionne pas :/
Y aurait il quelqu'un qui ait une idee sur le pourquoi du fait que cela ne fonctionne pas ?
Ou alors une idee sur un moyen de regler ce probleme, ou le contourner, etc.
Merci d'avance !
Guillaume
Partager