The following script already works pretty good. I can validate my given xml
file against the xsd
file and log all validation errors.
My issue is, I want to move the xml
file, if no validation error occurs. But I don't know where to place the move-item
statement. I always get the error file can not moved because it is still in use.
Here is the code. Hopefully someone can help.
# 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
The created xmlReader gets closed, before i try to move the file. But the file is still blocks and I don't know why.
Ok, try this new rewrite of your code.
Of course, I cannot see what your function logger
does exactly, but I can't imagine that is causing the file to stay locked.
I did change the code now to initialize all validation settings variables on top AND adde this: $valSettings.CloseInput = $true
, to make sure the underlying stream or TextReader gets closed. See 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