Анализ 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