2009-05-05 105 views
22

作爲我的開發的一部分,我希望能夠針對單個XSD文件驗證整個文件夾的XML文件的價值。一個PowerShell函數似乎是一個很好的候選人,因爲我可以像這樣管理一個文件列表:dir * .xml | Validate-Xml -Schema。\ MySchema.xsd如何使用PowerShell根據XSD驗證XML文件?

我考慮從Validating an Xml against Referenced XSD in C#問題移植C#代碼,但我不知道如何在PowerShell中添加處理程序。

+0

爲什麼你需要它是PowerShell只是因爲你正在閱讀標準輸入文件列表? – 2009-05-05 02:25:08

+2

我希望能夠輕鬆地將其集成到自動構建腳本中。不想編譯一個應用程序只是爲了做到這一點。一個PowerShell腳本似乎很適合這種事情。擴展的 – 2009-05-05 03:33:17

回答

11

我寫一個PowerShell功能做到這一點:

用法:

DIR * .XML |測試的XML -schema「 \ MySchemaFile.xsd」 - 命名‘http://tempuri.org

代碼:

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" 
} 
} 
+0

該腳本有錯誤。它沒有用於該功能的右大括號。 – OnesimusUnbound 2010-01-18 08:08:18

+0

''閱讀器'應該在while循環後關閉。否則,在Finalizer-saftey-net啓動之前,您將無法編輯該文件。 – 2010-09-01 11:11:51

+0

這似乎不起作用:PS D:\ projects \ svcs> dir * .xml | Test-Xml 術語'Test-Xml'不被識別爲cmdlet,函數,腳本文件或可操作程序的名稱。 – Chloe 2012-07-06 15:52:15

13

PowerShell Community Extensions有一個Test-Xml cmdlet。唯一的缺點是這些擴展沒有被更新一段時間,但是大多數擴展都在最新版本的PowerShell(包括Test-Xml)上工作。只要做一個Get-Childitem's並將該列表傳遞給一個foreach,每個調用Test-Xml。

+1

v1.2已發佈,以支持PowerShell的v2版本。他們似乎都工作得很好,所以我不確定是否有任何缺點。 – 2010-01-21 03:25:54

8

我想評論說,在當前接受的答案中的腳本不驗證關於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

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

我寫了另一個版本,可以將此錯誤報告:

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 \實驗室的XML驗證> dir test.xml |試驗XMLFILE test.xsd

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

你的答案很棒,簡短有效:)你只是想''schemareader.Dispose()'這會導致模式文件鎖 – Adassko 2018-01-09 14:08:39

+0

謝謝;我已經用更新後的版本更新了我的答案,這個更新的版本被寫爲一個可以包含在模塊中並支持管道的功能。 – wangzq 2018-01-09 22:41:52

2

我使用這個簡單的代碼片段,總是工作,你並不需要複雜的功能。它這個例子中我加載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 
    }) 
+0

這是最簡單的(因此通常是最好的)解決方案。唯一的問題是它不適用於具有非空字符串的目標名稱空間的模式。要處理這種情況,您必須單獨加載XmlSchema對象。 – ssamuel 2013-05-28 23:45:23

2

(Flatliner DOA)的解決方案是工作好上PSV2,但不能在Server 2012的PSv3。

(wangzq)的解決方案正在PS2和PS3上工作!

人誰需要對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 
0

我重新寫了(我知道壞習慣中),但@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 
} 

功能不再管道作爲替代使用文件路徑,這是一個複雜這個用例並不需要。隨意破解開始/進程/結束處理程序。

1

我意識到這是一個老問題,但我嘗試了所提供的答案,無法讓他們在Powershell中成功運行。

我已經創建了以下使用此處描述的一些技術的函數。我發現它非常可靠。

我不得不在不同時間驗證XML文檔,但是我總是發現行號爲0.看來XmlSchemaException.LineNumber只會在加載文檔時可用。

如果你這樣做以後使用Validate()方法上XmlDocument驗證則LineNumber上/ LinePosition將始終爲0

相反,你應該做的驗證,同時使用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 
}