Lecture de fichier xml (optimisation) [Résolu]

Signaler
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008
-
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008
-
Bonjour.
Je ne sais pas si je dois mettre ma question dans ce thème ou dans le thème base de données. Désolé si ce n'est pas au bon endroit.

J'ai un souci sur une lecture de fichier xml. (a partir d'un code en VBA)
Je dois récupérer le contenu d'une base de donnée pour mettre les informations sous une certaine forme sous une feuille excel.

Voici la structure du fichier xml:

    <SDBHeader>
        <Copyright>Reproduction interdite</Copyright>
        <FileName>File.xml</FileName>
        <Data Version="01.00" Date="2007/10/20 11:40:40.000"/>
    </SDBHeader>
    <NumParam Mnemo="toto" Type="A" Desc="Desc toto">
        <Length>32</Length>
        <Cv>GD</Cv>
        <Tube Default="TubeA">
        </Tube>
        <Comment="Mnemo toto"/>
    </NumParam>
    <StatParam Mnemo="titi" Type="B" Desc="Desc Titi">
        <Length>1</Length>
        <Cv>FC</Cv>
        <Tube Default="TubeD">
        </Tube>
    </StatParam>
    <NumParam Mnemo="tata" Type="D" Desc="Desc Tata">
        <Length>8</Length>
        <Cv>VB</Cv>
        <Tube Default="Tube3">
        </Tube>
        <Comment="Mnemo Tata"/>
    </NumParam>

Le problème est que l'exécution du code est beaucoup trop longue compte tenu de l'énormité du fichier xml.

Voici l'algo que j'utilise pour l'instant (je charge l'arbre dom et j'accède a tous mes éléments par une boucle for qui parcoure les différents items):

Dim xDoc As MSXML.DOMDocument
    Dim xDomEl As MSXML.IXMLDOMElement
    Dim siNbTag As Single
    Dim siTagNum As Single
    Dim aTagNameTab(2)
    Dim iIndex As Integer
   
    Dim sParamCode  As String
    Dim sType As String
    Dim sTube As String
    Dim sLength As String
    Dim sCv As String
    Dim sComment As String
    Dim iNbChildTag As Integer
   

    Set xDoc = New MSXML.DOMDocument
    xDoc.validateOnParse = False
   
    aTagNameTab(1) = "NumParam"
    aTagNameTab(2) = "StringParam"
   
    If xDoc.Load(PathFileXml) Then
        For iIndex = 1 To 2
            '**** exploitation des tags NumParam ou StringParam (suivant l'index de la boucle)

            siNbTag = xDoc.getElementsByTagName(aTagNameTab(iIndex)).Length
            If siNbTag > 0 Then
                 For siTagNum = 0 To siNbTag - 1
                    '**** récupération des données propre a chaque paramètre
                    sParamCode = xDoc.getElementsByTagName(aTagNameTab(iIndex)).Item(siTagNum).Attributes.getNamedItem("Mnemo").Text
                    sType = xDoc.getElementsByTagName(aTagNameTab(iIndex)).Item(siTagNum).Attributes.getNamedItem("Type").Text
                   
                    Set xDomEl = xDoc.getElementsByTagName(aTagNameTab(1, iIndex)).Item(siTagNum)
                    sLength = xDomEl.getElementsByTagName("Length").Item(0).Text
                    sCv = xDomEl.getElementsByTagName("Cv").Item(0).Text
                    sTube = xDomEl.getElementsByTagName("TransferFunctions").Item(0).Attributes.getNamedItem("Default").Text
                   
                    iNbChildTag = xDomEl.getElementsByTagName("Comment").Length
                    If iNbChildTag > 0 Then
                         sComment = xDomEl.getElementsByTagName("Comment").Item(0).Text
                    End If

                    '**** Ecriture des données dans une feuille excel
                    Call WriteInfo(sParamCode, sType, sLength, sCv, sTube, sComment)
                Next
            End If
        Next
    End If

Ca fonctionne, mais ca met beaucoup trop de temps. Je cherche donc a changer mon algo.
J'ai cherché a parcourir en séquence l'arbre dom en testant les tags un par un pour récupérer l'info (sans m'occuper du numéro d'index des items), mais je n'ai pas trouvé comment faire. J'ai bien trouvé une méthode 'nextNode' que j'aurais aimé utiliser mais elle n'est pas disponible sur l'objet 'Element' que j'utilise. Quelqu'un pourrait-il m'aider en me disant quels objets et méthodes utiliser?

Merci d'avance.

12 réponses

Messages postés
6063
Date d'inscription
dimanche 13 avril 2003
Statut
Modérateur
Dernière intervention
15 juillet 2011
27
les requetes xpath permettent de faire des  interrogations plus pousser donc plus rapide.
Tu peut prendre directement un attribut
ou un critre sur un attribut
http://jerome.developpez.com/xmlxsl/xpath/

bon courage
Messages postés
3877
Date d'inscription
mardi 19 mars 2002
Statut
Membre
Dernière intervention
23 août 2018
18
J'avais déjà fait des recherches sur XML, il y a un bon bout de temps et je n'ai jamais donné suite par après, mais bon, voici la façon dont "il" procédait. C'est assez long à récupérer (± 5 secondes pour 10 colonnes sur 1800 lignes) puisque je laisse l'affichage (ScreenUpdating), mais ça pourrait, je pense, être optimisé...(?)

J'ai laissé les noms d'objets tels que déclarées à l'origine...

Public Sub LoadFichier()
        Dim oDoc As msXML.DOMDocument
        Dim fSuccess As Boolean
        Dim oRoot As msXML.IXMLDOMNode
        Dim oCountry As msXML.IXMLDOMNode
       
        On Error GoTo HandleErr
       
        Filename = OuvrirFichier("xml")
        If Filename = "" Then Exit Sub
       
        Set oDoc = New msXML.DOMDocument
       
        ' Load the  XML from disk, without validating it. Wait
        ' for the load to finish before proceeding.
        oDoc.async = False
        oDoc.validateOnParse = False
              
        fSuccess = oDoc.Load(Filename)
       
        ' If anything went wrong, quit now.
        If Not fSuccess Then Exit Sub
      
        Cells.ClearContents
       
        ' Get the root of the XML tree.
        Set oRoot = oDoc.documentElement
        
        ' Inscrire les entêtes
        For I = 0 To oRoot.childNodes.Item(1).childNodes.Length - 1
            Range("A1").Offset(0, I) = oRoot.childNodes.Item(1).childNodes.Item(I).nodeName
        Next
       
        For Each oCountry In oRoot.childNodes          I 0: J J + 1
          Set oChildren = oCountry.childNodes
          For Each oChild In oChildren
            Range("A1").Offset(J, I) = oChild.Text
            I = I + 1
          Next
        Next
       
Exit Sub
HandleErr:
        MsgBox "Error " & Err.Number & ": " & Err.Description
End Sub

Peut-être est-ce plus rapide que ta méthode (?)

MPi²
Pour ceux qui programment sous Office, n'oubliez pas qu'il existe un forum dédié à ces applications VBA....... ICI
Messages postés
6063
Date d'inscription
dimanche 13 avril 2003
Statut
Modérateur
Dernière intervention
15 juillet 2011
27
Salut

Utilises des requetes xpath c'est plus optimisé

Bon courage
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

Merci a toi nhervagault.
Je vais me documenter un peu sur les requètes xpath (que je ne connais pas du tout) et je reviendrai accepter la réponse.
J'espère seulement que c'est disponible sous VBA Excel.
En tout cas, encore merci pour cette rapide réponse.
Messages postés
6063
Date d'inscription
dimanche 13 avril 2003
Statut
Modérateur
Dernière intervention
15 juillet 2011
27
C'est dispo dans MSXML.IXMLDOMElement
tu faisxDoc.SelectNodes("expPath") ou xDoc.SelectSingleNode("expPath")
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

Merci encore pour ton aide et désolé pour la réponse tardive mais j'ai été bloqué sur autre chose.
Bref, j'ai en effet pu utiliser xDoc.SelectNodes mais a part si je n'y ai rien compris, je ne vois pas trop en quoi il va améliorer mes temps d'exécution car il me renvoie (en gros) la même chose que mon feu xDoc.getElementsByTagName et du coup mon code ne va pas plus vite avec ca. C'est peut-être plus rapide en effet mais j'ai l'impression que la grosse lenteur de mon code vient plus de l'utilisation de la boucle For qui parcourt les différents Item. Donc, en faisant le test, je n'ai pas obtenu d'amélioration.
Soit je n'ai pas compris comment utiliser tes méthodes a mon avantage, soit il me faudrait autre chose.
Le hic, est que je ne cherche pas a accéder a un seul champ mais a tous les "lister". (et yen a autour de 40000 du type NumParam et StatParam réunis)
Donc, il me faudrait plutot un moyen de passer d'un tag courant de niveau 'n', au tag suivant de niveau 'n' (de même parent quoi), sans s'occuper de la position ou l'on se trouve dans le fichier. ( pour s'affranchir du Item(iIndex) qui semble rallonger. )
Mais c'est peut-être une fausse piste.
J'en suis même venu a me demander (mais ca me parait ahurissant!!), vu que je dois forcément parcourir tous le fichier, si ca ne prnedrait pas moins de temps de parcourir ce fichier en séquence en le traitant comme un fichier ascii normal.
Bref, je patoge toujours donc si quelqu'un a une solution ou si nhervagault a une autre piste, tout est bienvenue.
Merci et si on se recroise pas ici, bon week end a tous.
Messages postés
3877
Date d'inscription
mardi 19 mars 2002
Statut
Membre
Dernière intervention
23 août 2018
18
Je ne suis pas vraiment certain, puisque je ne travaille pas avec xml, mais je pense que la lenteur vient du fait que tu inscris tes données à chaque tour de boucle... Peut-être pourrais-tu stocker tes infos dans un tableau (array) et une fois toutes les données lues, tu les inscris directement dans la feuille Excel (?)

Quelle version d'Excel utilises-tu ?
Et ton code est-il uniquement sous Excel ?

MPi²
Pour ceux qui programment sous Office, n'oubliez pas qu'il existe un forum dédié à ces applications VBA....... ICI
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

@ nhervagault: Merci beaucoup pour le lien. Il est très interessant et m'apprend plus sur les différentes requètes que je pourrais utiliser (du style following ou d'autres du même style). C'est très puissant en fait. Je vais de ce pas faire des essais. Par contre, la puissance des requètes xPath est d'accéder rapidement aux éléments de l'arbre et je peux, comme tu l'as dit, "prendre directement un attribut ou un critere sur un attribut". Le problème, c'est que je veux presque tout récupérer et donc je ne pense pas utiliser toute la puissance qu'apporte ces requètes dans le cas d'un accés direct au cas par cas.

@ MPi: Bonjour et merci de t'être penché sur mon souci. Mais hélas, j'avais pensé au problème d'écriture dans excel a chaque boucle. J'ai donc fait le test en désactivant toute les écritures dans les feuilles et en gardant juste le parcours de l'arbre et la récupération des infos. Je n'ai même pas stocké les infos dans un tableau (donc ce n'est pas ca qui prend du temps non plus). Et malheureusement, le temps mis pour l'exécution a été très semblable (quelques minuscules secondes de gagner sur les11 premières minutes de fonctionnement- j'ai arrété ensuite-). La lenteur ne vient donc pas de là.
Sinon, j'utilise un vieil Excel: Excel 2000 (et ce serait bien si le code pouvait être compatible 97) et oui, tout mon code est sous excel.

Voila. Je vais faire des essais grace aux liens de nhervagault. Ca devrait améliorer un peu mais je ne pense pas avoir trouver réellement mon bonheur en y réfléchissant bien.... On verra.

Merci a vous 2 en tout cas.
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

@ nhervagault: Aaaargh!! Il veut pas m'utiliser toutes les requètes que j'ai vu sur ton lien!!! Il me sort des syntax error sur certaines. Je vais continuer a investiguer.

@ MPi:
En effet, je peux pas prendre ton code tel quel, mais c'est vrai que j'ai toujours préféré l'utilisation des 'for each'. Je sais pas pourquoi je n'y ai pas pensé.
En faisant une compilation des méthodes, du style, un 'for each' a la place de ma boucle 'for' et les requètes xPath pour cibler ensuite mes éléments a l'intérieur, j'arriverai peut-être a un bon gain.

Merci a vous deux, je vais essayer cet après-midi et je vous tiens au courant.
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

Bonjour a vous.
Je vous remercie pour votre aide.
Grace a vous, j'ai réussi a descendre de 2h30 d'exécution a 30 minutes. (en optant pour un mix des 2 solutions comme je l'avais dit).
30 minutes, c'est encore trop long et je vais essayer de changer encore certaines choses en espérant gagner un petit peu dans chaque boucle. On verra.
J'espère descendre en dessous des 20 minutes.
En tout cas merci pour votre aide a tous les 2 (je sais pas si on peut accepter 2 réponses mais on verra ^^)
Et si vous avez encore des conseils sur le sujet qui vous vienne, n'hésitez pas car je n'abandonne pas.

A bientot.
Messages postés
6063
Date d'inscription
dimanche 13 avril 2003
Statut
Modérateur
Dernière intervention
15 juillet 2011
27
Pour info
Les requetes xpath ne fonctionnent pas car  ton doc xml n'est pas valide.

La balise  comment  est mauvaise  il faut soit mettre une balise soit nomme l'attribut
il y a un problème sur les deux comments

exemple 1 /Parameters/NumParam/Cv
exemple 2 /Parameters/NumParam[@Type='A']
exemple 3 //Tube[@Default='TubeD']/..
...

As-toi de chercher ce que tu veux
Car je ne sais pas trop ce que tu cherches que j'ai pas analysé ton code en profondeur
Messages postés
29
Date d'inscription
mercredi 25 avril 2007
Statut
Membre
Dernière intervention
22 avril 2008

Merci pour ta remarque nhervagault.
C'est vrai que la balise n'est pas bonne. C'était une petite erreur. Mais le comportement était le même avec un nom d'attribut.
Enfin, pour l'instant, ca passe avec le mix que j'ai fait donc on va voir.
Merci en tout cas.