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:
PS > Set-strictMode -version latest
et maintenant on va charger notre fragment xml dans la mémoire.
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'
PS > $docs = $docX.GetElementsByTagName('doc')
je comptes les objets de cette collection:
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:
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:
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:
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':
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'
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()
1 2 3 4
| PS > # exemple
PS > $comobject = New-Object -ComObject WScript.shell
PS > $spf = $comobject.SpecialFolders
PS > $spf[0] |
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 |
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:
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'
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.
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>
'@ |
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
} |
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.
Partager