Анализ XML-файла с помощью скрипта powershell

У меня есть файл журнала xml. Файл выглядит так:

      <Transaction name='0' id='1'>
    <Response>Warning</Response>
    <Statistic mode='Element'>
      <Information>0</Information>
      <Warning>0</Warning>
      <Error>0</Error>
    </Statistic>
    <Messages>
      <Message state='Warning'>Personal-Nr.: 12345, Tom Test</Message>
      <Message state='Warning'>This is a warning message 1</Message>
      <Message state='Warning'>This is a warning message 2</Message>
      <Message state='Warning'>This is a warning message 3</Message>
      <Message state='Warning'>This is a warning message 4</Message>
    </Messages>
</Transaction>

Этот шаблон повторяется около 900 раз, иногда с большим или меньшим количеством сообщений. Теперь я просто хочу получить все транзакции, где <Response>Error</Response>имеет место.

Поэтому я сделал этот код в Powershell:

      ## parsing xml file and opening inner node
Select-Xml -Path C:\Users\user\path\path\file.xml -XPath '/Paths/Task/Transaction' | ForEach-Object { $_.Node.InnerXML }

## looping through Response set with include="Error"
$_.Node.InnerXML | Where-Object Response -eq 'Error' | ForEach-Object { $_.Messages }
echo $_.Messages

Но единственные данные, которые я получаю, это все транзакции, независимо от того, является ли ответ Warningили же . Более того, даже не важно, если я оставлю только Select-Xmlстроку и удалить остальные. Результат всегда один и тот же. Я всегда получаю ВСЕ ответы.

Итак, мой вопрос: как мне получить только те транзакции, где есть ответ?

Бонусный вопрос: есть ли возможность просто иметь первую строку сообщения каждого Errorтранзакция как выход? Чтобы у меня был список всех Personal-Nrчто было в ошибочной транзакции?

Большое спасибо

1 ответ

Выложенные вами операторы на данный момент полностью самостоятельны - первый выводит текстовую кодировку всех узлов транзакций, а второй и третий просто ничего не делают , т.к. $_больше не имеет значения, присвоенного ему в этой точке.

Чтобы правильно «соединить» их, вам нужно либо поместить логику фильтрации внутри первого ForEach-Objectблокировать, например:

      Select-Xml ... |ForEach-Object {
  if($_.Node.Response -eq 'Error'){ $_.Messages }
}

... или сохранить вывод каждого шага во промежуточной переменной, например:

      $allTransactions = Select-Xml ... -XPath '//Transaction' 

$allTransactions |ForEach-Object {
  if($_.Node.Response -eq 'Error'){ $_.Messages }
}

Я должен отметить, что ForEach-Object { if(...){ $_ } }немного анти-шаблон, если только ваш код не имеет более сложных побочных эффектов - более идиоматичным решением было бы вызвать Where-Objectкомандлет для фильтрации вывода:

      $allTransactions |Where-Object {
  $_.Node.Response -eq 'Error'
} |ForEach-Object Messages

Хотя эти предложения могут решить вашу проблему, я настоятельно рекомендую не делать ничего из этого — XPath гораздо более эффективен , чем то, для чего вы его сейчас используете :)


Как мне получить только транзакции, в которых ответ «Ошибка»?

Я бы предложил упростить ваш код, используя более точное выражение XPath с - тем, которое ищет именно то, что вы хотите:

      Select-Xml -Path C:\Users\user\path\path\file.xml -XPath '/Paths/Task/Transaction[Response = "Error"]'

Есть ли возможность просто иметь первую строку сообщения каждой транзакции «Ошибка» в качестве вывода? Чтобы у меня был список всех "Личных-Nr", которые были в ошибочной транзакции?

Конечно!

И снова самый простой способ — изменить выражение XPath, на этот раз разрешая только первый <Message>узел под <Transaction>соответствует указанным выше критериям:

      # beware that index selectors in XPath start at 1, not 0
//Transaction[Response = "Warning"]/Messages/Message[1]

Но это не все! XPath имеет несколько полезных функций, так что мы можем пойти еще дальше и получить XPathизвлеките и расшифруйте текст сообщения и для нас!

      //Transaction[Response = "Warning"]/Messages/Message[1]/text()

Это приведет к возврату набора узлов, состоящего из XmlTextэкземпляры, которые вы можете преобразовать непосредственно в строки, чтобы получить необработанное содержимое строки.

Собираем обратно вместе с Select-Xml, вы получите что-то вроде этого:

      $filePath = 'C:\Users\user\path\path\file.xml'
$xPath = '//Transaction[Response = "Warning"]/Messages/Message[1]/text()'
$messages = Select-Xml -Path $filePath -XPath $xPath |ForEach-Object ToString

$messages
Другие вопросы по тегам