Почему XDocument.Descendants() возвращает IEnumerator в PowerShell ISE?
-
22-08-2019 - |
Вопрос
Я пишу скрипт PowerShell для управления некоторым XML-файлом установщика Windows (WiX).Для этого я использую новые XML-API в .NET 3.5, поскольку нахожу, что с этим API работать проще, чем с DOM.Следующий фрагмент скрипта категорически отказывается работать:
[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
Сценарий следует выводите каждый <String> тег в отдельной строке.Я собираюсь заставить его сделать что-нибудь более интересное, как только эта ошибка будет устранена ;-)
XML-документ представляет собой стандартный файл локализации 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 не является $null (я явно протестировал это), и если я напишу-host $wxl, я увижу, что документ был загружен.Передача $strings в Get-Member возвращает ошибку, указывающую, что "Ни один объект не был указан для get-member", а write-host $strings ничего не делает.Я также попробовал $wxl.Descendants("WixLocalization") с теми же результатами.Такие вещи, как $wxl.Root и $wxl.Nodes, работают должным образом.Отлаживая с помощью PowerShell ISE, я вижу, что для $strings было установлено значение IEnumerator, а не ожидаемое значение IEnumerable<XElement>.Тестирование IEnumerator с помощью одного MoveNext, а затем Current указывает, что "Current = ", предположительно, $ null.
Самое странное, что та же техника сработала в предыдущем сценарии.Точно такой же код, но с разными именами переменных и строковыми литералами.И, только что попробовав отладить и этот скрипт (чтобы проверить поведение), похоже, что теперь он также отображает то же поведение.
Решение
Эта проблема заинтриговала меня, поэтому я немного поискал вокруг.После долгой возни с PowerShell и поиска в Интернете я нашел ваше решение.
За сам код нужно отдать должное Джейми Томсону.http://dougfinke.com/blog/index.php/2007/08/07/using-xmllinq-in-powershell/
Недостающая часть заключается в обработке пространства имен в XML-файле.Вот код, который должен работать у вас:
[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
}
Другие советы
Я знаю, вы сказали, что предпочитаете не работать с DOM, но есть ли какая-то причина, по которой у вас это не работает?
[xml]$test = gc .\test.wml
$test
$test.WixLocalization
$test.WixLocalization.String
Это выводит:
PS> $test.WixLocalization.Строка
Идентификатор #текст
-- -----
0 Рекламный опубликованный ресурс
1 Выделение места в реестре
На данный момент предварительное изучение этого не должно быть слишком сложным.