简体   繁体   English

如何使用PowerShell针对XSD验证XML文件?

[英]How do I use PowerShell to Validate XML files against an XSD?

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. 作为我开发的一部分,我希望能够针对单个XSD文件验证整个文件夹的XML文件。 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 | PowerShell函数似乎是一个很好的候选者,因为我可以像这样管道一个文件列表:dir * .xml | Validate-Xml -Schema .\\MySchema.xsd Validate-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中添加处理程序。

The PowerShell Community Extensions has a Test-Xml cmdlet. PowerShell社区扩展具有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). 唯一的缺点是扩展还没有更新一段时间,但大多数都在最新版本的PowerShell(包括Test-Xml)上工作。 Just do a Get-Childitem's and pass the list to a foreach, calling Test-Xml on each. 只需做一个Get-Childitem并将列表传递给foreach,在每个上面调用Test-Xml。

I wrote a PowerShell function to do this: 我写了一个PowerShell函数来做到这一点:

Usage: 用法:

dir *.xml | dir * .xml | Test-Xml -Schema ".\\MySchemaFile.xsd" -Namespace " http://tempuri.org " 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"
}
}

I want to comment that the script in current accepted answer doesn't validate errors about incorrect orders of elements of xs:sequence . 我想评论当前接受的答案中的脚本不验证有关xs:sequence元素的错误顺序的错误。 For example: test.xml 例如: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 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 | PS C:\\ temp \\ lab-xml-validation> dir test.xml | Test-XmlFile test.xsd Test-XmlFile test.xsd

System.Xml.Schema.XmlSchemaValidationException: The element 'address' has invalid child element 'name'.
...

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
    })

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

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.LineNumber will only be available while loading the document. 我不得不在不同时间之前验证XML文档,但是我总是发现行号为0.看起来XmlSchemaException.LineNumber仅在加载文档时可用。

If you do validation afterwards using the Validate() method on an XmlDocument then LineNumber/LinePosition will always be 0. 如果之后使用XmlDocument上的Validate()方法进行Validate()则LineNumber / LinePosition将始终为0。

Instead you should do validation while reading using an XmlReader and adding a validation event handler to a block of script. 相反,您应该在使用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
}

I have created a separate PowerShell file which can perform XSD validation on XML files with an inline schema reference. 我创建了一个单独的PowerShell文件,该文件可以使用内联模式引用对XML文件执行XSD验证。 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 下载和如何在https://knowledge.zomers.eu/PowerShell/Pages/How-to-validate-XML-against-an-XSD-schema-using-PowerShell.aspx上获得

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. 随意破解开始/处理/结束处理程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM