Из архивного файла измените все XML-файлы, чтобы удалить узлы, содержащие определенное значение @Name - PullRequest
0 голосов
/ 17 июня 2019

У меня есть архивный файл (похожий на файл .zip или .7z), содержащий несколько файлов XML, используя общую структуру:

<DataRequest>
  <DataObject>
    <DataProperty Name= "prop1">
      <SimpleProperty Value="abc"
    </DataProperty>
    <DataProperty Name= "propB">
      <SimpleProperty Value="123"
    </DataProperty>
    <DataProperty Name= "propX">
      <SimpleProperty Value="xyz"
    </DataProperty>
  </DataObject>
</DataRequest>

Я бы хотел массово изменить эти файлы, чтобы удалить все <DataProperty> узлы с параметром @Name = "prop1".

EDIT Согласно ответу @AnsgarWiechers ниже, я обновил свой рабочий код

$zipFileName = "C:\Users\me\filename.bxp"
Add-Type -Assembly  System.IO.Compression.FileSystem
$zip = [System.IO.Compression.ZipFile]::Open($zipfileName, "Update")
$editFile = $zip.Entries.Where({$_.Name -Like "*.xml"})
$xml = New-Object Xml

$editFile | ForEach-Object {
    $xml.Load($_.FullName)
    $xml.SelectNodes('//DataProperty[@Name="prop1"]') |
        ForEach-Object { $_.ParentNode.RemoveChild($_) }
    $xml.Save('C:\path\to\output.xml')
}

Проблема здесь в том, как файл сохраняется. Я не хочу выводить новый XML-файл. Скорее я хотел бы изменить файл .xml в том виде, в котором он находится в архиве .bxp.

В настоящее время этот процесс выполняется вручную. В окне проводника я могу щелкнуть правой кнопкой мыши по файлу .bxp и выбрать «7zip -> Open Archive», чтобы открыть редактор «Блокнот». Любые изменения, сохраненные в этом редакторе, будут влиять на файл, который находится внутри .bxp. Я хочу воспроизвести этот процесс программно.

Если это невозможно, я могу вывести файл .xml и добавить его в архив вручную. В этом случае моей проблемой будет изменение имени выходного файла для каждого XML-файла в цикле. Могу ли я установить переменную, которая будет наследовать имя файла, и вывести то же имя файла? Это то, что делает переменная $_.FullName?

1 Ответ

1 голос
/ 18 июня 2019

Ваш синтаксис неверен. pipe соединяет вывод одного оператора / команды со входом другого оператора / команды. Вы можете переносить длинную строку после символа канала, потому что PowerShell распознает, что оператор продолжается на следующей строке. Но если вы перенесете строку до , символ конвейера PowerShell не сможет автоматически определять продолжение строки и интерпретировать обе строки по отдельности, поскольку первая строка содержит законченный, допустимый оператор, что приводит к синтаксису Вы заметили ошибку.

Если вы хотите обернуть строку перед каналом, вы должны экранировать символ новой строки в конце первой строки с помощью обратного кавычка (`).

$xml.SelectNodes('//DataProperty') `
    | ForEach-Object { $_.ParentNode.RemoveChild($_) }

Но, опять же, лучше обернуть строку после символом трубы:

$xml.SelectNodes('//DataProperty') |
    ForEach-Object { $_.ParentNode.RemoveChild($_) }

Кроме того, нет причин для установки трубы между $xml = [xml](Get-Content $_.FullName) и $xml.SelectNodes('//DataProperty'). Первый оператор присваивает проанализированную структуру данных XML переменной, которая затем используется во втором операторе. Второе утверждение не читает входные данные конвейера. Технически вы можете передать проанализированные данные XML в конвейер, но для этого потребуется еще ForEach-Object вокруг SelectNodes().

Можно также привести аргумент, что

$xml = New-Object Xml
$xml.Load($_.FullName)

- более чистый способ загрузки данных XML, чем

$xml = [xml](Get-Content $_.FullName)

но в большинстве сценариев оба подхода будут работать.

С учетом всех проблем с синтаксисом фильтр в языке XPath является выражением в квадратных скобках. В вашем случае вы хотите удалить узлы, которые имеют атрибут Name со значением prop1, поэтому фильтр будет выглядеть так: [@Name="prop1"].

Измените свой код на что-то вроде этого:

$xml = New-Object Xml
$editFile | ForEach-Object {
    $xml.Load($_.FullName)
    $xml.SelectNodes('//DataProperty[@Name="prop1"]') |
        ForEach-Object { $_.ParentNode.RemoveChild($_) }
}

и он должен делать то, что вы ожидаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...