salut,



dans ce petit thread, on ne va rien faire, seulement connaitre le sujet 'Z' et surtout 'suivre son raisonnement'.

le sujet 'Z' est un membre de DEV qui est entré dans le monde de PS et a maintenant un simple traitement à faire, qui se consiste à créer des élements 'doc' dans un fragement xml déja existant...suivant le:


alors je commences par la bonne pratique:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
PS > Set-strictMode -version latest
et maintenant on va charger notre fragment xml dans la mémoire.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
PS > $docX = [xml] @'
 <docs>
   <doc>doc 1</doc>
   <doc>doc 2</doc>
   <doc>doc 3</doc>
   <doc>doc 4</doc>
 </docs>
'@
c'est simple comme tout et maintenant on va selectionner les noeuds 'doc'

Code : Sélectionner tout - Visualiser dans une fenêtre à part
PS > $docs = $docX.GetElementsByTagName('doc')
je comptes les objets de cette collection:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
PS > $docs.length

La propriété «*length*» est introuvable sur cet objet. Vérifiez qu'elle existe.
Au niveau de ligne*: 1 Caractère*: 7
+ $docs. <<<< length
    + CategoryInfo          : InvalidOperation: (.:OperatorToken) [], RuntimeE
   xception
    + FullyQualifiedErrorId : PropertyNotFoundStrict
mais qu'est-ce qui se passe ? ou est donc passé la propriété 'length' qui est une propriété-Alias pour les types de base System.Array comme est decrit dans le fichier de configuration:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
PS D:\> cat $pshome/types.ps1xml | Select-String "system.array" -co 0,6

>         <Name>System.Array</Name>
          <Members>
              <AliasProperty>
                  <Name>Count</Name>
                  <ReferencedMemberName>Length</ReferencedMemberName>
              </AliasProperty>
          </Members>

voyant le premier element:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
PS> $docs[0]
Impossible d'indexer dans un objet de type System.Xml.XmlElementList.
Au niveau de ligne*: 1 Caractère*: 7
+ $docs[ <<<< 0]
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : CannotIndex
et en plus ça ne s'indexe pas ?!

je vais essayer avec une autre manière:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
PS> $docs |select -first 1 -expand '#text'
doc 1
le message d'erreur mentionne que le type System.Xml.XmlElementList ne peux pas être indexé, ou du moins de cette manière, car au final c'est une liste et une liste doit pouvoir être indexé d'une facon ou d'une autre.

je vais chercher les membres de cette collection 'XmlElementList':

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
PS> get-member -input $docs
....
....
Item          Method                System.Xml.XmlNode Item(int index)
Count         Property              System.Int32 Count {get;}

j'essayes avec la propriété Count

et maintenant la méthode 'Item'

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
PS> $docs.item(0).innertext
doc 1

donc, cette collection n'est pas une simple liste et à ce que je vois System.Array n'est pas son type de base.
Comme beaucoup d'objet COM on ne peux pas acceder a ses elements qu'avec la méthode item()

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
PS > # exemple
PS > $comobject = New-Object -ComObject WScript.shell
PS > $spf = $comobject.SpecialFolders
PS > $spf[0]
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
Impossible d'indexer dans un objet de type System.__ComObject.
Au niveau de ligne*: 1 Caractère*: 6
+ $spf[ <<<< 0]
    + CategoryInfo          : InvalidOperation: (0:Int32) [], RuntimeException
    + FullyQualifiedErrorId : CannotIndex

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
PS > $spf.Item(0)
D:\Documents and Settings\All Users\Bureau

je continue maintenant mon traitement, on veux ajouter d'autres elements 'doc' au fragment xml $docX au nombre des 'doc' déja existant, ici on a 4 elements 'doc' donc il faut ajouter 4 autres 'doc' pour que le resultat devienne au final:

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
 <docs>
   <doc>doc 1</doc>
   <doc>doc 2</doc>
   <doc>doc 3</doc>
   <doc>doc 4</doc>
   <doc>doc 5</doc>
   <doc>doc 6</doc>
   <doc>doc 7</doc>
   <doc>doc 8</doc>
 </docs>
je crois que je vais utiliser une boucle 'for'

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
for($i=0; $i -lt $docs.count; $i++) {
   $child = $docX.CreateElement("doc")
   $child.innerText = "doc $($docs.count + 1)"
   $nul=$docX.docs.appendChild($child)
   write-verbose "ajout de $($child.OuterXml)" -verbose
}
mais qu'es-ce qui se passe ? c'est une boucle infini !!
en théorie elle va nous ajouter un nombre défini d'elements 'doc' (ici 4) mais en réalité c'est une boucle infini.

en réalité une collection DOM XmlElementList est une collection "vivante" c'est à dire qu'elle se mets à jour automatiquement si le document change, elle n'est pas donc statique comme les autres collections, de ce fait "$docs.count" s'incremente à chaque ajout de nouveaux elements 'doc', donc la loupe devient une loupe infini.

comment remdier à ce problème ?

je vais essayer de mettre le contenu du '$docs.count' dans une variable en dehors du contexte de la loupe, ainsi, je crois, que la collection sera mise à jour une seul fois à la fin de la boucle.


Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
PS > $docX = [xml] @'
 <docs>
   <doc>doc 1</doc>
   <doc>doc 2</doc>
   <doc>doc 3</doc>
   <doc>doc 4</doc>
 </docs>
'@
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
$docs = $docX.GetElementsByTagName('doc')

$len = $docs.count
for($i=0; $i -lt $len; $i++) {
   $child = $docX.CreateElement("doc")
   $child.innerText = "doc $($docs.count + 1)"
   $nul=$docX.docs.appendChild($child)
   write-verbose "ajout de $($child.OuterXml)" -verbose
}
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
PS > $docX.CreateNavigator().OuterXml

<docs>
  <doc>doc 1</doc>
  <doc>doc 2</doc>
  <doc>doc 3</doc>
  <doc>doc 4</doc>
  <doc>doc 5</doc>
  <doc>doc 6</doc>
  <doc>doc 7</doc>
  <doc>doc 8</doc>
</docs>
Bingo !!

au final le sujet 'Z' a resolu son probléme après quelques tests et recherches..

Remarque:
l'utilisation des proprités d'objets dans le contexte d'une boucle est une mauvaise pratique comme l'a montré ce thread, j'ai voulu vous montré qu'en plus du problème de performance il y a aussi un problème de mise à jour des collections vivantes du DOM.