如何使用 PowerShell 根据 XSD 验证 XML 文件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/822907/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use PowerShell to Validate XML files against an XSD?
提问by Flatliner DOA
As a part of my development I'd like to be able to validate an entire folder's worth of XML files against a single XSD file. A PowerShell function seems like a good candidate for this as I can then just pipe a list of files to it like so: dir *.xml | Validate-Xml -Schema .\MySchema.xsd
作为我开发的一部分,我希望能够针对单个 XSD 文件验证整个文件夹中的 XML 文件的价值。PowerShell 函数似乎是一个很好的候选者,因为我可以像这样将文件列表通过管道传递给它: dir *.xml | 验证-Xml -Schema .\MySchema.xsd
I've considered porting C# code from the Validating an Xml against Referenced XSD in C#question, but I don't know how to Add handlers in PowerShell.
我已经考虑过从 C#问题中针对引用的 XSD 验证 Xml移植 C# 代码,但我不知道如何在 PowerShell 中添加处理程序。
采纳答案by Flatliner DOA
I wrote a PowerShell function to do this:
我编写了一个 PowerShell 函数来执行此操作:
Usage:
用法:
dir *.xml | Test-Xml -Schema ".\MySchemaFile.xsd" -Namespace "http://tempuri.org"
目录 *.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"
}
}
回答by eddiegroves
The PowerShell Community Extensionshas 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.
在PowerShell的社区扩展有一个测试的XML cmdlet的。唯一的缺点是扩展有一段时间没有更新,但大多数都在最新版本的 powershell 上工作(包括 Test-Xml)。只需执行 Get-Childitem 并将列表传递给 foreach,对每个调用 Test-Xml。
回答by wangzq
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
我想评论一下,当前接受的答案中的脚本不会验证有关xs:sequence. 例如: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
测试.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
PS C:\temp\lab-xml-validation> dir test.xml | 测试-XmlFile test.xsd
System.Xml.Schema.XmlSchemaValidationException: The element 'address' has invalid child element 'name'.
...
回答by Diomos
I am using this simple snippet, always works and you don't need complicated functions. It this example I am loading configuration xml with data which are used later for deployment and server configuration:
我正在使用这个简单的代码片段,它始终有效,而且您不需要复杂的功能。在这个例子中,我正在加载带有数据的配置 xml,这些数据稍后用于部署和服务器配置:
# You probably don't need this, it's just my way
$script:Context = New-Object -TypeName System.Management.Automation.PSObject
Add-Member -InputObject $Context -MemberType NoteProperty -Name Configuration -Value ""
$ConfigurationPath = $(Join-Path -Path $PWD -ChildPath "Configuration")
# Load xml and its schema
$Context.Configuration = [xml](Get-Content -LiteralPath $(Join-Path -Path $ConfigurationPath -ChildPath "Configuration.xml"))
$Context.Configuration.Schemas.Add($null, $(Join-Path -Path $ConfigurationPath -ChildPath "Configuration.xsd")) | Out-Null
# Validate xml against schema
$Context.Configuration.Validate(
{
Write-Host "ERROR: The Configuration-File Configuration.xml is not valid. $($_.Message)" -ForegroundColor Red
exit 1
})
回答by emekm
the solution of (Flatliner DOA) is working good on PSv2, but not on Server 2012 PSv3.
(Flatliner DOA) 的解决方案在 PSv2 上运行良好,但不适用于 Server 2012 PSv3。
the solution of (wangzq) is working on PS2 and PS3!!
(wangzq) 的解决方案正在PS2和PS3上运行!!
anyone who needs an xml validation on PS3, can use this (based on wangzq's function)
任何需要在 PS3 上进行 xml 验证的人都可以使用它(基于 wangzq 的功能)
function Test-Xml {
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[string] $XmlFile,
[Parameter(Mandatory=$true)]
[string] $SchemaFile
)
[string[]]$Script:XmlValidationErrorLog = @()
[scriptblock] $ValidationEventHandler = {
$Script:XmlValidationErrorLog += $args[1].Exception.Message
}
$xml = New-Object System.Xml.XmlDocument
$schemaReader = New-Object System.Xml.XmlTextReader $SchemaFile
$schema = [System.Xml.Schema.XmlSchema]::Read($schemaReader, $ValidationEventHandler)
$xml.Schemas.Add($schema) | Out-Null
$xml.Load($XmlFile)
$xml.Validate($ValidationEventHandler)
if ($Script:XmlValidationErrorLog) {
Write-Warning "$($Script:XmlValidationErrorLog.Count) errors found"
Write-Error "$Script:XmlValidationErrorLog"
}
else {
Write-Host "The script is valid"
}
}
Test-Xml -XmlFile $XmlFile -SchemaFile $SchemaFile
回答by Koen Zomers
I have created a separate PowerShell file which can perform XSD validation on XML files with an inline schema reference. Works really well. Download and howto are available on https://knowledge.zomers.eu/PowerShell/Pages/How-to-validate-XML-against-an-XSD-schema-using-PowerShell.aspx
我创建了一个单独的 PowerShell 文件,该文件可以使用内联架构引用对 XML 文件执行 XSD 验证。效果很好。下载和操作方法可在https://knowledge.zomers.eu/PowerShell/Pages/How-to-validate-XML-against-an-XSD-schema-using-PowerShell.aspx 获得
回答by CarlR
I realise this is an old question however I tried the answers provided and could not get them to work successfully in Powershell.
我意识到这是一个老问题,但是我尝试了提供的答案,但无法让它们在 Powershell 中成功运行。
I have created the following function which uses some of the techniques described here. I have found it very reliable.
我创建了以下函数,它使用了这里描述的一些技术。我发现它非常可靠。
I had to validate XML documents before at various times however I always found the line number to be 0. It appears the XmlSchemaException.LineNumberwill only be available while loading the document.
我之前曾多次验证 XML 文档,但我总是发现行号为 0。看来XmlSchemaException.LineNumber只有在加载文档时才可用。
If you do validation afterwards using the Validate()method on an XmlDocumentthen LineNumber/LinePosition will always be 0.
如果您之后使用该Validate()方法进行验证,XmlDocument则 LineNumber/LinePosition 将始终为 0。
Instead you should do validation while reading using an XmlReaderand adding a validation event handler to a block of script.
相反,您应该在使用 an 读取XmlReader并将验证事件处理程序添加到脚本块时进行验证。
Function Test-Xml()
{
[CmdletBinding(PositionalBinding=$false)]
param (
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[string] [ValidateScript({Test-Path -Path $_})] $Path,
[Parameter(Mandatory=$true)]
[string] [ValidateScript({Test-Path -Path $_})] $SchemaFilePath,
[Parameter(Mandatory=$false)]
$Namespace = $null
)
[string[]]$Script:XmlValidationErrorLog = @()
[scriptblock] $ValidationEventHandler = {
$Script:XmlValidationErrorLog += "`n" + "Line: $($_.Exception.LineNumber) Offset: $($_.Exception.LinePosition) - $($_.Message)"
}
$readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings
$readerSettings.ValidationType = [System.Xml.ValidationType]::Schema
$readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings
$readerSettings.Schemas.Add($Namespace, $SchemaFilePath) | Out-Null
$readerSettings.add_ValidationEventHandler($ValidationEventHandler)
try
{
$reader = [System.Xml.XmlReader]::Create($Path, $readerSettings)
while ($reader.Read()) { }
}
#handler to ensure we always close the reader sicne it locks files
finally
{
$reader.Close()
}
if ($Script:XmlValidationErrorLog)
{
[string[]]$ValidationErrors = $Script:XmlValidationErrorLog
Write-Warning "Xml file ""$Path"" is NOT valid according to schema ""$SchemaFilePath"""
Write-Warning "$($Script:XmlValidationErrorLog.Count) errors found"
}
else
{
Write-Host "Xml file ""$Path"" is valid according to schema ""$SchemaFilePath"""
}
Return ,$ValidationErrors #The comma prevents powershell from unravelling the collection http://bit.ly/1fcZovr
}
回答by Conrad B
I re-wrote it (I know bad habbit) , but the starting script by @Flatliner_DOA was too good to discard completely.
我重新编写了它(我知道坏习惯),但是@Flatliner_DOA 的起始脚本太好了,不能完全丢弃。
function Test-Xml {
[cmdletbinding()]
param(
[parameter(mandatory=$true)]$InputFile,
$Namespace = $null,
[parameter(mandatory=$true)]$SchemaFile
)
BEGIN {
$failCount = 0
$failureMessages = ""
$fileName = ""
}
PROCESS {
if ($inputfile)
{
write-verbose "input file: $inputfile"
write-verbose "schemafile: $SchemaFile"
$fileName = (resolve-path $inputfile).path
if (-not (test-path $SchemaFile)) {throw "schemafile not found $schemafile"}
$readerSettings = New-Object -TypeName System.Xml.XmlReaderSettings
$readerSettings.ValidationType = [System.Xml.ValidationType]::Schema
$readerSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessIdentityConstraints -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings
$readerSettings.Schemas.Add($Namespace, $SchemaFile) | Out-Null
$readerSettings.add_ValidationEventHandler(
{
try {
$detail = $_.Message
$detail += "`n" + "On Line: $($_.exception.linenumber) Offset: $($_.exception.lineposition)"
} catch {}
$failureMessages += $detail
$failCount = $failCount + 1
});
try {
$reader = [System.Xml.XmlReader]::Create($fileName, $readerSettings)
while ($reader.Read()) { }
}
#handler to ensure we always close the reader sicne it locks files
finally {
$reader.Close()
}
} else {
throw 'no input file'
}
}
END {
if ($failureMessages)
{ $failureMessages}
write-verbose "$failCount validation errors were found"
}
}
#example calling/useage code follows:
$erroractionpreference = 'stop'
Set-strictmode -version 2
$valid = @(Test-Xml -inputfile $inputfile -schemafile $XSDPath )
write-host "Found ($($valid.count)) errors"
if ($valid.count) {
$valid |write-host -foregroundcolor red
}
The function no longer pipelines as an alternative to using a file-path, it's a complication this use-case does not need. Feel free to hack the begin/process/end handlers away.
该函数不再作为使用文件路径的替代方法进行管道传输,这是该用例不需要的复杂性。随意破解开始/进程/结束处理程序。

