¿Por qué es XDocument.Descendants () regresan IEnumerator en PowerShell ISE?
-
22-08-2019 - |
Pregunta
Estoy escribiendo un script de PowerShell para manipular algunos Windows Installer XML (WiX). Estoy usando las nuevas API XML en .NET 3.5 para hacer esto, ya que me parece que es una API más fácil de trabajar que el DOM. El fragmento siguiente secuencia de comandos se niega a trabajar rotundamente:
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
# Basic idea: Iterate through en.wxl's l10ns, get string for each l10n, search all wxs files for that value.
pushd "C:\temp\installer_l10n"
$wxlFileName = "${pwd}\en.wxl"
$wxl = [System.Xml.Linq.XDocument]::Load($wxlFileName)
$strings = $wxl.Descendants("String")
$strings
$strings | foreach {
$_
}
popd
El script debe salida de cada etiqueta
El documento XML es un archivo de localización estándar WiX:
<?xml version="1.0" encoding="utf-8" ?>
<WixLocalization Culture="en-US" xmlns="http://schemas.microsoft.com/wix/2006/localization">
<String Id="0">Advertising published resource</String>
<String Id="1">Allocating registry space</String>
...
</WixLocalization>
$ strings $ no es nulo (He probado de forma explícita), y si el huésped escribo $ wxl, puedo ver que el documento se ha cargado. $ Tuberías cadenas en Get-Member devuelve un error que indica que "Ningún objeto se ha especificado para obtener miembros" y escribir en host $ strings no hace nada. También he intentado $ wxl.Descendants ( "WixLocalization") con los mismos resultados. Cosas como $ wxl.Root y $ wxl.Nodes funcionan como se esperaba. Depuración con PowerShell ISE, veo que $ strings se ha establecido a IEnumerator, en lugar de la esperada IEnumerable
Lo extraño es que la misma técnica trabajó en un guión previo. El mismo código, pero con diferentes nombres de variables y literales de cadena. Y después de haber intentado depurar simplemente que el guión también (para verificar el comportamiento), parece que es ahora también se muestra el mismo comportamiento.
Solución
Este problema me ha intrigado, así que lo hice algunas búsquedas alrededor. Después de mucho andar por ahí en PowerShell y buscar en la web me encontré con su solución.
El crédito va a Jamie Thomson para el código real. http://dougfinke.com/blog/ index.php / 2007/08/07 / usando-xmllinq-en-powershell /
La pieza que falta es manejar el espacio de nombres en el archivo XML. Aquí está el código que debería funcionar para usted:
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml.Linq") | Out-Null
# Basic idea: Iterate through en.wxl's l10ns, get string for each l10n, search all wxs files for that value.
$wxlFileName = "${pwd}\en.wxl"
$wxl = [System.Xml.Linq.XDocument]::Load($wxlFileName)
$ns = [System.Xml.Linq.XNamespace]”http://schemas.microsoft.com/wix/2006/localization”
$strings = $wxl.Descendants($ns + "String")
foreach ($string in $strings) {
$string
}
Otros consejos
Sé que dijo que no preferimos trabajar con el DOM, pero ¿hay alguna razón por la que esto no funciona para usted?
[xml]$test = gc .\test.wml
$test
$test.WixLocalization
$test.WixLocalization.String
Esto da salida:
PS> $ test.WixLocalization.String
Id #text
- -----
0 Publicidad recurso publicado
1 La asignación de espacio de registro
Foreach'ing sobre eso no debería ser demasiado difícil en ese punto.