Por que é XDocument.Descendants () retornando IEnumerator no PowerShell ISE?
-
22-08-2019 - |
Pergunta
Eu estou escrevendo um script PowerShell para manipular alguns Installer XML do Windows (WiX). Estou usando as novas APIs XML em .NET 3.5 para fazer isso, como eu encontrá-lo uma API mais fácil de trabalhar do que o DOM. O seguinte fragmento de script é simplesmente se recusar a trabalhar:
[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
O script deve saída de cada
O documento XML é um arquivo WiX localização padrão:
<?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>
$ cordas não é $ null (Eu testei-lo explicitamente), e se eu escrever-host $ LxC, eu posso ver que o documento foi carregado. Tubulação $ strings em Get-Member retorna um erro informando que "Nenhum objeto foi especificado para get-member" e write-Host $ cordas não faz nada. Eu também tentei $ wxl.Descendants ( "WixLocalization"), com os mesmos resultados. Coisas como $ wxl.Root e trabalho $ wxl.Nodes como esperado. Depuração com o PowerShell ISE, vejo que $ cordas foi definido para IEnumerator, ao invés do IEnumerable esperado
O estranho é que a mesma técnica trabalhou em um script anterior. Exatamente o mesmo código, mas com diferentes nomes de variáveis ??e strings literais. E, tendo apenas tentei depurar o script também (para verificar o comportamento), parece que agora está também exibindo o mesmo comportamento.
Solução
Este problema me intrigou, então eu fiz alguma pesquisa ao redor. Depois de muito andar em PowerShell e pesquisar na web eu encontrei a solução.
O crédito vai para Jamie Thomson para o código real. http://dougfinke.com/blog/ index.php / 2007/08/07 / utilizando-xmllinq-in-powershell /
A peça que falta é lidar com o namespace no arquivo XML. Aqui está o código que deve funcionar para você:
[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
}
Outras dicas
Eu sei que você disse que não preferiu trabalhar com o DOM, mas há qualquer razão para que isso não funciona para você?
[xml]$test = gc .\test.wml
$test
$test.WixLocalization
$test.WixLocalization.String
Esta saídas:
PS> $ test.WixLocalization.String
Id #text
- -----
0 Publicidade recurso publicado
1 a alocação de espaço de registro
Foreach'ing sobre isso não deve ser muito difícil nesse ponto.