How do I use PowerShell to Validate XML files against an XSD?
I want to comment that the script in current accepted answer doesn't validate errors about incorrect orders of elements of xs:sequence
. For example:test.xml
<addresses xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation='test.xsd'> <address> <street>Baker street 5</street> <name>Joe Tester</name> </address></addresses>
test.xsd
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'> <xs:element name="addresses"> <xs:complexType> <xs:sequence> <xs:element ref="address" minOccurs='1' maxOccurs='unbounded'/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="address"> <xs:complexType> <xs:sequence> <xs:element ref="name" minOccurs='0' maxOccurs='1'/> <xs:element ref="street" minOccurs='0' maxOccurs='1'/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="name" type='xs:string'/> <xs:element name="street" type='xs:string'/> </xs:schema>
I wrote another version that can report this error:
function Test-XmlFile{ <# .Synopsis Validates an xml file against an xml schema file. .Example PS> dir *.xml | Test-XmlFile schema.xsd #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [string] $SchemaFile, [Parameter(ValueFromPipeline=$true, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] [alias('Fullname')] [string] $XmlFile, [scriptblock] $ValidationEventHandler = { Write-Error $args[1].Exception } ) begin { $schemaReader = New-Object System.Xml.XmlTextReader $SchemaFile $schema = [System.Xml.Schema.XmlSchema]::Read($schemaReader, $ValidationEventHandler) } process { $ret = $true try { $xml = New-Object System.Xml.XmlDocument $xml.Schemas.Add($schema) | Out-Null $xml.Load($XmlFile) $xml.Validate({ throw ([PsCustomObject] @{ SchemaFile = $SchemaFile XmlFile = $XmlFile Exception = $args[1].Exception }) }) } catch { Write-Error $_ $ret = $false } $ret } end { $schemaReader.Close() }}
PS C:\temp\lab-xml-validation> dir test.xml | Test-XmlFile test.xsd
System.Xml.Schema.XmlSchemaValidationException: The element 'address' has invalid child element 'name'....
The PowerShell Community Extensions has a Test-Xml cmdlet. The only downside is the extensions havn't been updated for awhile, but most do work on the lastest version of powershell (including Test-Xml). Just do a Get-Childitem's and pass the list to a foreach, calling Test-Xml on each.
I wrote a PowerShell function to do this:
Usage:
dir *.xml | Test-Xml -Schema ".\MySchemaFile.xsd" -Namespace "http://tempuri.org"
Code:
function Test-Xml {param( $InputObject = $null, $Namespace = $null, $SchemaFile = $null)BEGIN { $failCount = 0 $failureMessages = "" $fileName = ""}PROCESS { if ($InputObject -and $_) { throw 'ParameterBinderStrings\AmbiguousParameterSet' break } elseif ($InputObject) { $InputObject } elseif ($_) { $fileName = $_.FullName $readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings $readerSettings.ValidationType = [System.Xml.ValidationType]::Schema $readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings $readerSettings.Schemas.Add($Namespace, $SchemaFile) | Out-Null $readerSettings.add_ValidationEventHandler( { $failureMessages = $failureMessages + [System.Environment]::NewLine + $fileName + " - " + $_.Message $failCount = $failCount + 1 }); $reader = [System.Xml.XmlReader]::Create($_, $readerSettings) while ($reader.Read()) { } $reader.Close() } else { throw 'ParameterBinderStrings\InputObjectNotBound' }}END { $failureMessages "$failCount validation errors were found"}}