Следующий скрипт уже работает довольно хорошо. Я могу сверить данный файл xml с файлом xsd и зарегистрировать все ошибки проверки.
Моя проблема в том, что я хочу переместить файл xml, если не возникает ошибка проверки. Но я не знаю, где разместить утверждение move-item. Я всегда получаю ошибку file can not moved because it is still in use. Вот код. Надеюсь, кто-то может помочь.
# Get all XML files in the folder that start with "products"
$xmlFiles = Get-ChildItem -Path $sourceDir -Filter products*.xml -File
foreach ($xmlFile in $xmlFiles) {
$path = $xmlFile.FullName #$sourceDir + $xmlFile
# Checking if file exits
if (-not (Test-Path $path)) {
Write-Warning "The XML file $path does not exist."
logger -ErrMessage "File not exits." -log_file $logfile -xml_file $path
# Checking if file is readable
} elseif ($xmlFile.IsReadOnly) {
Write-Warning "XML file not readable: $path"
logger -ErrMessage "File not readable." -log_file $logfile -xml_file $path
} else {
# Set up validation settings
$valSettings = [System.Xml.XmlReaderSettings]::new();
$valSettings.ValidationType = [System.Xml.ValidationType]::Schema;
$valSettings.CheckCharacters = $true;
$valSettings.ValidationFlags = ([System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings)
$valSettings.Schemas.Add($URL, $xsdFilename);
# Store validation errors in an array
$errors = New-Object System.Collections.Generic.List[System.Xml.Schema.ValidationEventArgs]
# Create a validation event handler
$validationHandler = {
param ([object] $sender, [System.Xml.Schema.ValidationEventArgs] $e)
$lineNumber = ""
$linePosition = ""
# Get the line number and position of the error
# if ($e.Exception) {
$lineNumber = $e.Exception.LineNumber
$linePosition = $e.Exception.LinePosition
# }
if ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Warning) {
Write-Host("WARNING___: ")
$errors.Add($e)
Write-Warning($e.Message, $lineNumber)
Write-Host("--Line: $lineNumber, Position: $linePosition")
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
elseif ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Error) {
if ($e.Exception -and $e.Exception.Message.StartsWith("The 'text' element is not declared.")) {
$e.Severity = [System.Xml.Schema.XmlSeverityType]::Warning
Write-Host("WARNING___: ")
$errors.Add($e)
Write-Warning($e.Exception.Message)
Write-Host("--Line: $lineNumber, Position: $linePosition")
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
else {
$errors.Add($e)
Write-Warning $e.Message;
Write-Host("--Line: $lineNumber, Position: $linePosition");
if (![string]::IsNullOrEmpty($logfile)) {
#write-host $e.Exception.GetType().FullName
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
}
}
}
# Set up validation event handler to capture errors
#$valSettings.ValidationEventHandler += {$errors.Add($_)}#/ turn it on, if 'Add_ValidationEventHandler' runs into error
$destination = "C:\Users\BeKa-Coca\Downloads\XML_Validation\$xmlFile"
try {
# Read each xml node, validate it and close the xml file
$valSettings.Add_ValidationEventHandler($validationHandler)
$xmlReader = [System.Xml.XmlReader]::Create([System.IO.File]::OpenRead($path), $valSettings);
while ($xmlReader.Read()) { }
$xmlReader.Close()
#Copy-Item $xmlFile.FullName $destination
#Move-Item $xmlFile.FullName $destination -Force
} catch{
$lineNumber = ""
$linePosition = ""
# if ($_.Exception) {
$lineNumber = [regex]::Match($_.Exception.Message, "Zeile (\d+)").Groups[1].Value
$linePosition = [regex]::Match($_.Exception.Message, "Position (\d+)").Groups[1].Value
# }
$errorDesc = $_.Exception.Message
write-warning $_.Exception.Message
Write-Host("--Line: $lineNumber, Position: $linePosition")
logger -ErrMessage $errorDesc -line $lineNumber -log_file $logfile -xml_file $path
}
finally {
# make sure the xml reader is closed even if an exception occurs
if ($xmlReader) {
$xmlReader.Dispose()
# Copy / move only valid xml files
If (-not($errors)) {
#Copy-Item $xmlFile.FullName $destination -Force
Move-Item $xmlFile.FullName $destination -Force
} else {WRITE-Host "ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"}
}
}
} #End If-else
#Copy-Item $xmlFile.FullName $destination
#logger -ErrMessage "The file is in use by another process." -log_file $logfile -xml_file $path
} #End ForEach Loop
Созданный xmlReader закрывается, прежде чем я попытаюсь переместить файл. Но файл по-прежнему блокируется, и я не знаю, почему.
[5] Вместо тестирования if ($xmlReader -ne $null) просто сделайте if ($xmlReader)
Я изменил свой код, но перемещение файла по-прежнему невозможно. Я получаю сообщение об ошибке xml file is locked by another process. куда поставить $xmlFile.IsReadOnly? Это ключ, чтобы заставить его работать правильно, не сталкиваясь с ошибкой?
Возможно, вы изменили код, но не сообщение на SO, поэтому мы не можем сказать, что вы сделали. Замените if ((Get-Item $path).IsReadOnly) на if ($xmlFile.IsReadOnly)
Что касается данных советов, я редактирую свой оригинальный пост. Но он все еще сталкивается с той же ошибкой: file could not be moved, because it is locked by another process. Почему xmlReader.close() не закрывает object правильно? Чтобы запустить скрипт, я использую следующую команду в консоли Windows . ./xml_validation_logging_v02.ps1. Может из-за этого ошибка?





Хорошо, попробуйте эту новую переработку вашего кода.
Конечно, я не могу видеть, что именно делает ваша функция logger, но я не могу представить, что из-за этого файл остается заблокированным.
Я изменил код сейчас, чтобы инициализировать все переменные настроек проверки сверху И добавить это: $valSettings.CloseInput = $true, чтобы убедиться, что базовый поток или TextReader закрывается. Смотрите CloseInput
# Set up validation settings
$valSettings = [System.Xml.XmlReaderSettings]::new()
$valSettings.ValidationType = [System.Xml.ValidationType]::Schema
$valSettings.CheckCharacters = $true
$valSettings.CloseInput = $true # close the underlying stream or TextReader when the reader is closed
$valSettings.ValidationFlags = ([System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings)
$valSettings.Schemas.Add($URL, $xsdFilename)
# Store validation errors in an List object
$errors = New-Object System.Collections.Generic.List[System.Xml.Schema.ValidationEventArgs]
# find all xml files in the source directory
$xmlFiles = Get-ChildItem -Path $sourceDir -Filter 'products*.xml' -File
foreach ($xmlFile in $xmlFiles) {
$errors.Clear()
$path = $xmlFile.FullName
if ($xmlFile.IsReadOnly) {
Write-Warning "XML file not readable: $path"
logger -ErrMessage "File not readable." -log_file $logfile -xml_file $path
continue # skip this file and proceed with the next
}
# Create a validation event handler
$validationHandler = {
param ([object] $sender, [System.Xml.Schema.ValidationEventArgs] $e)
# Get the line number and position of the error
$lineNumber = $e.Exception.LineNumber
$linePosition = $e.Exception.LinePosition
if ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Warning) {
Write-Host "WARNING___: "
[void]$errors.Add($e)
Write-Warning ($e.Message, $lineNumber)
Write-Host "--Line: $lineNumber, Position: $linePosition"
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
elseif ($e.Severity -eq [System.Xml.Schema.XmlSeverityType]::Error) {
if ($e.Exception.Message -like "The 'text' element is not declared*") {
Write-Host "ERROR___: "
[void]$errors.Add($e)
Write-Warning $e.Exception.Message
Write-Host "--Line: $lineNumber, Position: $linePosition"
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
else {
[void]$errors.Add($e)
Write-Warning $e.Message
Write-Host "--Line: $lineNumber, Position: $linePosition"
# write-host $e.Exception.GetType().FullName
logger -ErrMessage $e.Message -line $lineNumber -log_file $logfile -xml_file $path
}
}
}
$destination = 'C:\Users\BeKa-Coca\Downloads\XML_Validation' # just the path here
try {
# Read each xml node, validate it and close the xml file
$valSettings.Add_ValidationEventHandler($validationHandler)
$xmlReader = [System.Xml.XmlReader]::Create([System.IO.File]::OpenRead($path), $valSettings)
while (!$xmlReader.EOF -and $xmlReader.Read()) {}
$xmlReader.Dispose()
$xmlReader = $null
# Copy / move only valid xml files
if ($errors.Count -eq 0) {
#$xmlFile | Copy-Item -Destination $destination
$xmlFile | Move-Item -Destination $destination -Force
}
}
catch{
$lineNumber = [regex]::Match($_.Exception.Message, "Zeile (\d+)").Groups[1].Value
$linePosition = [regex]::Match($_.Exception.Message, "Position (\d+)").Groups[1].Value
$errorDesc = $_.Exception.Message
Write-Warning $errorDesc
Write-Host "--Line: $lineNumber, Position: $linePosition"
logger -ErrMessage $errorDesc -line $lineNumber -log_file $logfile -xml_file $path
}
finally {
# make sure the xml reader is closed even if an exception occurs
if ($xmlReader) { $xmlReader.Dispose() }
$xmlReader = $null
}
} # End foreach loop
Спасибо за помощь. Но он все еще работает с той же ошибкой. Файл заблокирован и не может быть перемещен. + $xmlFile | Move-Item -Destination $destination -Force + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : WriteError: (C:\Users\BeKa-C..._230505_401.xml:FileInfo) [Move-Item], IOException + FullyQualifiedErrorId : MoveFileInfoItemIOError,Microsoft.PowerShell.Commands.MoveItemCommand Еще какие советы по решению проблемы? Это как-то связано с командой, которую я использую для запуска скрипта . .\xml_validation_logging_v03.ps1?
Спасибо. Оно работает. Правильные файлы перемещаются. Вот функция регистратора: function logger { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string]$ErrMessage, [Parameter(Mandatory=$false)] [string]$line, [Parameter(Mandatory=$true)] [string]$log_file, [Parameter(Mandatory=$false)] [string]$xml_file ) $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $logMsg = "[$timestamp] -- [Source: $xml_file] -- [Line: $line] -- [ERROR] $ErrMessage" Add-Content -Path $log_file -Value $logMsg }
Есть одна последняя проблема. Если $xmlFiles = Get-ChildItem -Path $sourceDir -Filter 'products*.xml' -File получить более одного XML-файла, одна ошибка проверки будет зарегистрирована дважды. Я проверяю только один файл, регистрация ошибок работает правильно и регистрирует только одну ошибку. Думаю, это как-то связано с петлей.
Я сделал еще пару тестов. Пока у меня есть только один XML-файл для проверки, все работает так, как ожидалось, и каждая ошибка получает один раз. Но если я получаю в папке два xml-файла, все ошибки для второго файла регистрируются дважды. Что может быть причиной этой проблемы?
Хотите проверить это еще раз? Я был бы признателен за ваши отзывы. Хорошего дня.
@the_floor Я внес небольшие изменения. Если вы по-прежнему получаете двойные сообщения в журнале, не могли бы вы опубликовать их полностью
Большой! Потрясающе. Оно работает. Большое спасибо. Что касается обработки ошибок: есть ли что-то еще, что имеет смысл также регистрировать, чтобы улучшить обработку ошибок? Наслаждайтесь оставшейся частью дня.
@the_floor Ах, приятно слышать, что ваша функция ведения журнала выглядит нормально. Если вы считаете, что мой ответ решил вашу проблему, отметьте этот вопрос как «готово». Вы можете увидеть в туре как это сделать.
На первый взгляд: [1] Если вы добавите
-Fileв строку Get-ChildItem, вы обязательно получите существующие объекты FileInfo (нет необходимости проверять, существует ли он) [2] Из-за [1] внутри вашей переменной цикла$xmlFileесть объект со многими свойствами, одним из которых является.FullName, поэтому используйте его для переменной$pathи используйте свойство$xmlFile.IsReadOnlyвместо того, чтобы снова получать элемент. [3]$xmlReader.Dispose()также закрывается, так что вы можете удалить$xmlReader.close()в блоке finally. [4] переместите строкуMove-Item ..из блока finally и поместите ее последней строкой в блокtry{}.