Я пересмотрел свой код с прошлого раза, чтобы встроить контроль ошибок, потому что я считаю, что именно по этой причине вы изменили while
l oop на foreach
l oop.
В приведенном ниже коде по-прежнему используется while l oop, потому что для меня это упрощает работу со счетчиками. Я изменил способ вставки шаблонов элементов и документов в код, потому что Here-Strings имеют тенденцию нарушать форматирование кода, что затрудняет его чтение. Теперь он использует расширение переменной отложенное , я опирался на этот ответ . Таким образом, шаблоны определяются на ранних этапах кода без нарушения отступов и расширяются при необходимости позже.
Я также изменил способ фиксации возможных ошибок. Теперь я использую один объект List для захвата всех типов ошибок и элементов, добавляя два дополнительных столбца к элементу ошибки: ErrorType
и ErrorDescription
.
$FREQUENCE_DECOMPTE = 'Nom="FREQUENCE_DECOMPTE" Valeur="MENS"'
$LIBELLE_ORGANISME = 'Nom="LIBELLE_ORGANISME" Valeur="HUMANIS CCN OG"'
$MONTANT_TOTAL = 'Nom="MONTANT_TOTAL" Valeur="0"'
$POLE = 'Nom="POLE" Valeur="1ADP"'
$CODE_ORGANISME = 'Nom="CODE_ORGANISME" Valeur="1ADP"'
# Create two template Here-Strings near the top of the code.
# The first one is a templete for every single item in the XML, the second one
# merges it together as a complete XML document.
# The Here-Strings use SINGLE quotes, so the variables inside are now NOT expanded.
# We'll do that later in the code using $ExecutionContext.InvokeCommand.ExpandString($itemTemplate)
$itemTemplate = @'
<Document>
<Index Nom="TITLE" Valeur="$renommage"/>
<Index Nom="NO_ASSURE" Valeur="$($item.U65B8_IDRP)"/>
<Index Nom="DEBUT_PERIODE" Valeur="$RecupDateFinTraitement"/>
<Index Nom="FIN_PERIODE" Valeur="$RecupDateFin30"/>
<Index $FREQUENCE_DECOMPTE/>
<Index $LIBELLE_ORGANISME/>
<Index $MONTANT_TOTAL/>
<Index Nom="DATE_GENERATION_DECOMPTE" Valeur="$RecupDateFinTraitement"/>
<Index $POLE/>
<Index $CODE_ORGANISME/>
<Index Nom="ALERTE_MAIL" Valeur="$alerte"/>
<Fichier Nom="$($item.U6618_FILENAME)"/>
</Document>
'@
$documentTemplate = @'
<?xml version="1.0" encoding="utf-8"?>
<Documents Origine="ERELEVE_HUM">
$($xmlItems -join "`r`n")
</Documents>
'@
# create a list object to capture any errors
$errorList = [System.Collections.Generic.List[object]]::new()
# older PowerShell versions use
# $errorList = New-Object -TypeName System.Collections.Generic.List[object]
# read the csv file
$liste = Import-Csv -path 'C:\temp\testH.csv' -Delimiter ';'
# get the total remaining records to process
$restant = $liste.Count
# set a maximum value of items for each resulting XML file.
# In real life, change this value to 5000
$maxItemsPerXml = 3
# set a xml output file counter and an item index counter
$xmlFileCount = 1
$currentItem = 0
# loop through all items
while ($restant -gt 0) {
$itemCount = [math]::Min($maxItemsPerXml, $restant)
$xmlItems = for ($i = 0; $i -lt $itemCount; $i++) {
$item = $liste[$i + $currentItem]
$errorsFound = $false
# test some of the fields
## UCB63_DATENUM
$date = Get-Date
# if no error, the date variable will be set to the date in this field
if (-not [datetime]::TryParseExact($item.UCB63_DATENUM, 'M/d/yy HH:mm', $null, 'None', [ref]$date)) {
# add an error object to the errorList
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'BadDate'}},
@{Name = 'ErrorDescription'; Expression = {'UCB63_DATENUM has invalid date format'}}, *))
$errorsFound = $true
}
## U6618_FILENAME
if ([System.IO.Path]::GetExtension($item.U6618_FILENAME) -ne '.pdf'){
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'BadExtension'}},
@{Name = 'ErrorDescription'; Expression = {'U6618_FILENAME not a PDF file'}}, *))
$errorsFound = $true
}
## UF6E8_CANAL
if ([string]::IsNullOrWhiteSpace($item.UF6E8_CANAL)){
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'EmptyField'}},
@{Name = 'ErrorDescription'; Expression = {'UF6E8_CANAL is empty'}}, *))
$errorsFound = $true
}
## U65B8_IDRP
if ([string]::IsNullOrWhiteSpace($item.U65B8_IDRP)) {
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'EmptyField'}},
@{Name = 'ErrorDescription'; Expression = {'U65B8_IDRP is empty'}}, *))
$errorsFound = $true
}
if (!$errorsFound) {
$alerte = if ($item.UF6E8_CANAL -eq "ML") {1} else {0}
$renommage = [System.IO.Path]::GetFileNameWithoutExtension($item.U6618_FILENAME)
$RecupDateFinTraitement = $date.ToString('dd/MM/yyyy')
$RecupDateFin30 = $date.AddDays(30).ToString('dd/MM/yyyy')
# output each item in xml-style by (deferred) expansion of the variables that are now known
$ExecutionContext.InvokeCommand.ExpandString($itemTemplate)
}
else {
# the item had error(s). Increment the $itemCount variable,
# but don't let it grow beyond the $restant number of items!
$itemCount = [math]::Min(($itemCount + 1), $restant)
}
}
# create a complete file path and name for the output xml
$xmlFile = "C:\Temp\MIG_ERELEVE_MM_{0:dd-MM-yyyy}_{1:D3}.xml" -f (Get-Date), $xmlFileCount
# create the XML content, complete with declaration and root node and write it to file
$ExecutionContext.InvokeCommand.ExpandString($documentTemplate) | Set-Content -Path $xmlFile -Encoding UTF8
# increment the xml FILE counter
$xmlFileCount++
# update the csv ITEM counters
$restant -= $itemCount
$currentItem += $itemCount
}
# output the errors encountered if any
if ($errorList.Count) {
$errorList | Export-Csv -Path 'C:\temp\Errors.csv' -Delimiter ';' -NoTypeInformation -Encoding UTF8
}
По вашим наблюдениям, очевидно, PowerShell 4.0 плохо работает с $ExecutionContext.InvokeCommand.ExpandString()
.. Поэтому для этой версии (и более старых) используйте вместо этого:
$FREQUENCE_DECOMPTE = 'Nom="FREQUENCE_DECOMPTE" Valeur="MENS"'
$LIBELLE_ORGANISME = 'Nom="LIBELLE_ORGANISME" Valeur="HUMANIS CCN OG"'
$MONTANT_TOTAL = 'Nom="MONTANT_TOTAL" Valeur="0"'
$POLE = 'Nom="POLE" Valeur="1ADP"'
$CODE_ORGANISME = 'Nom="CODE_ORGANISME" Valeur="1ADP"'
# create a list object to capture any errors
$errorList = New-Object -TypeName System.Collections.Generic.List[object]
$errorList = New-Object -TypeName System.Collections.Generic.List[object]
# read the csv file
$liste = Import-Csv -path 'C:\temp\testH.csv' -Delimiter ';'
# get the total remaining records to process
$restant = $liste.Count
# set a maximum value of items for each resulting XML file.
# In real life, change this value to 5000
$maxItemsPerXml = 3
# set a xml output file counter and an item index counter
$xmlFileCount = 1
$currentItem = 0
# loop through all items
while ($restant -gt 0) {
$itemCount = [math]::Min($maxItemsPerXml, $restant)
$xmlItems = for ($i = 0; $i -lt $itemCount; $i++) {
$item = $liste[$i + $currentItem]
$errorsFound = $false
# test some of the fields
## UCB63_DATENUM
$date = Get-Date
# if no error, the date variable will be set to the date in this field
if (-not [datetime]::TryParseExact($item.UCB63_DATENUM, 'M/d/yy HH:mm', $null, 'None', [ref]$date)) {
# add an error object to the errorList
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'BadDate'}},
@{Name = 'ErrorDescription'; Expression = {'UCB63_DATENUM has invalid date format'}}, *))
$errorsFound = $true
}
## U6618_FILENAME
if ([System.IO.Path]::GetExtension($item.U6618_FILENAME) -ne '.pdf'){
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'BadExtension'}},
@{Name = 'ErrorDescription'; Expression = {'U6618_FILENAME not a PDF file'}}, *))
$errorsFound = $true
}
## UF6E8_CANAL
if ([string]::IsNullOrWhiteSpace($item.UF6E8_CANAL)){
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'EmptyField'}},
@{Name = 'ErrorDescription'; Expression = {'UF6E8_CANAL is empty'}}, *))
$errorsFound = $true
}
## U65B8_IDRP
if ([string]::IsNullOrWhiteSpace($item.U65B8_IDRP)) {
[void]$errorList.Add(($item | Select-Object @{Name = 'ErrorType'; Expression = {'EmptyField'}},
@{Name = 'ErrorDescription'; Expression = {'U65B8_IDRP is empty'}}, *))
$errorsFound = $true
}
if (!$errorsFound) {
$alerte = if ($item.UF6E8_CANAL -eq "ML") {1} else {0}
$renommage = [System.IO.Path]::GetFileNameWithoutExtension($item.U6618_FILENAME)
$RecupDateFinTraitement = $date.ToString('dd/MM/yyyy')
$RecupDateFin30 = $date.AddDays(30).ToString('dd/MM/yyyy')
# output each item in xml-style by (deferred) expansion of the variables that are now known
@"
<Document>
<Index Nom="TITLE" Valeur="$renommage"/>
<Index Nom="NO_ASSURE" Valeur="$($item.U65B8_IDRP)"/>
<Index Nom="DEBUT_PERIODE" Valeur="$RecupDateFinTraitement"/>
<Index Nom="FIN_PERIODE" Valeur="$RecupDateFin30"/>
<Index $FREQUENCE_DECOMPTE/>
<Index $LIBELLE_ORGANISME/>
<Index $MONTANT_TOTAL/>
<Index Nom="DATE_GENERATION_DECOMPTE" Valeur="$RecupDateFinTraitement"/>
<Index $POLE/>
<Index $CODE_ORGANISME/>
<Index Nom="ALERTE_MAIL" Valeur="$alerte"/>
<Fichier Nom="$($item.U6618_FILENAME)"/>
</Document>
"@
}
else {
# the item had error(s). Increment the $itemCount variable,
# but don't let it grow beyond the $restant number of items!
$itemCount = [math]::Min(($itemCount + 1), $restant)
}
}
# create a complete file path and name for the output xml
$xmlFile = "C:\Temp\MIG_ERELEVE_MM_{0:dd-MM-yyyy}_{1:D3}.xml" -f (Get-Date), $xmlFileCount
# create the XML content, complete with declaration and root node and write it to file
@"
<?xml version="1.0" encoding="utf-8"?>
<Documents Origine="ERELEVE_HUM">
$($xmlItems -join "`r`n")
</Documents>
"@ | Set-Content -Path $xmlFile -Encoding UTF8
# increment the xml FILE counter
$xmlFileCount++
# update the csv ITEM counters
$restant -= $itemCount
$currentItem += $itemCount
}
# output the errors encountered if any
if ($errorList.Count) {
$errorList | Export-Csv -Path 'C:\temp\Errors.csv' -Delimiter ';' -NoTypeInformation -Encoding UTF8
}
Некоторое пояснение:
while ($restant -gt 0)
l oop просто делает его намного проще, чем foreach($item in $liste)
, потому что он проверяет, есть ли еще элементы для обработки.
$itemCount = [math]::Min($maxItemsPerXml, $restant)
дает вам количество элементов, которые вы установили в $maxItemsPerXml
, но не больше, чем осталось элементов .
мы используем эту $itemCount
переменную в следующем l oop $xmlItems = for ($i = 0; $i -lt $itemCount; $i++)
, чтобы перебирать это максимальное количество элементов, и в то же время мы захватываем все, что l oop выводит в переменной $xmlItems
внутри этого l oop мы сначала выполняем несколько тестов для каждого поля для каждого элемента, и если тест не удался, мы сообщаем об этом в переменной $errorList
. Если элемент не прошел тесты, он исключается из вывода XML, поэтому нам нужно скорректировать переменную $itemCount
на $itemCount = [math]::Min(($itemCount + 1), $restant)
. Опять же, мы используем [math] :: Min (), чтобы убедиться, что мы никогда go не превысим оставшееся количество элементов.
если все тесты пройдены, элемент выводится как XML <Document>..</Document>
узлов и собраны в $xmlItems
тогда, если мы закончили for ($i = 0; $i -lt $itemCount; $i++)
l oop, мы собрали максимум $maxItemsPerXml
xml узлов и нам нужно сохранить XML.
после сохранения, нам нужно настроить различные счетчики, прежде чем мы повторно введем while l oop:
- счетчик файлов увеличивается для следующего файла на
$xmlFileCount++
- количество оставшихся элементов корректируется с помощью
$restant -= $itemCount
- индексный номер
$currentItem
устанавливается на следующее значение в общем массиве с $currentItem += $itemCount
наконец, мы проверяем, есть ли что-нибудь в $errorList
, и если да, мы пишем файл CSV C:\temp\Errors.csv
с все найденные нами ошибки
Все готово!