2009-05-05 36 views
22

Là một phần trong quá trình phát triển của tôi, tôi muốn có thể xác thực giá trị của toàn bộ thư mục của các tệp XML đối với một tệp XSD duy nhất. Một chức năng PowerShell có vẻ giống như một ứng cử viên tốt cho điều này vì tôi có thể sau đó chỉ cần ống một danh sách các tập tin vào nó như vậy: dir * .xml | Validate-Xml -Schema. \ MySchema.xsdLàm cách nào để sử dụng PowerShell để Xác thực các tệp XML với XSD?

Tôi đã xem xét chuyển mã C# từ câu hỏi Validating an Xml against Referenced XSD in C#, nhưng tôi không biết cách Thêm trình xử lý trong PowerShell.

+0

Tại sao chính xác bạn cần nó là PowerShell chỉ vì bạn đang đọc danh sách tệp từ stdin? –

+2

Tôi muốn có thể dễ dàng tích hợp nó vào tập lệnh tạo tự động. Không muốn phải biên dịch một ứng dụng chỉ để làm điều này. Một kịch bản PowerShell có vẻ giống như một sự phù hợp tự nhiên cho loại điều này. –

Trả lời

11

tôi đã viết một hàm PowerShell để làm điều này:

Cách sử dụng:

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

Tập lệnh có lỗi. Nó không có cú đúp đóng cho hàm. – OnesimusUnbound

+0

Bạn nên đóng '$ reader' sau vòng lặp' while'. Nếu không, bạn sẽ không thể chỉnh sửa tệp cho đến khi đá Finalizer-saftey bắt đầu. –

+0

Điều này dường như không hoạt động: PS D: \ projects \ svcs> dir * .xml | Test-Xml Thuật ngữ 'Test-Xml' không được nhận dạng là tên của lệnh ghép ngắn, hàm, tệp kịch bản hoặc chương trình có thể hoạt động. – Chloe

13

PowerShell Community Extensions có lệnh ghép ngắn Test-Xml. Nhược điểm duy nhất là các phần mở rộng không được cập nhật trong một thời gian, nhưng hầu hết làm việc trên phiên bản mới nhất của PowerShell (bao gồm Test-Xml). Chỉ cần làm một Get-Childitem của và vượt qua danh sách cho một foreach, gọi Test-Xml trên mỗi.

+1

v1.2 của các phần mở rộng đã được phát hành để hỗ trợ v2 của PowerShell. Tất cả chúng dường như hoạt động tốt nên tôi không chắc chắn về bất kỳ nhược điểm nào. –

8

Tôi muốn nhận xét rằng tập lệnh trong câu trả lời được chấp nhận hiện tại không xác thực lỗi về các đơn đặt hàng không chính xác của các phần tử xs:sequence. Ví dụ: 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> 

tôi đã viết một phiên bản khác có thể báo cáo lỗi này:

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 \ phòng thí nghiệm-xml -validation> dir test.xml | Test-XmlFile test.xsd

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

Câu trả lời của bạn rất tuyệt, ngắn và hiệu quả :) bạn chỉ bỏ lỡ '$ schemaReader.Dispose()' gây ra tệp lược đồ khóa – Adassko

+0

Cảm ơn; Tôi đã cập nhật câu trả lời của mình với phiên bản cập nhật được viết dưới dạng hàm có thể được đưa vào mô-đun và hỗ trợ đường dẫn. – wangzq

2

Tôi đang sử dụng đoạn mã đơn giản này, luôn hoạt động và bạn không cần các hàm phức tạp. Nó ví dụ này tôi đang tải cấu hình xml với các dữ liệu được sử dụng sau này cho việc triển khai và cấu hình server:

# 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

Đây là giải pháp đơn giản nhất (và do đó thường là tốt nhất). Vấn đề duy nhất là nó không làm việc cho một lược đồ có một không gian tên đích khác với chuỗi rỗng. Để xử lý trường hợp này, bạn phải tải riêng đối tượng XmlSchema. – ssamuel

2

các giải pháp của (Flatliner DOA) đang làm việc tốt trên PSv2, nhưng không phải trên Server 2012 PSv3.

giải pháp (wangzq) đang hoạt động trên PS2 và PS3 !!

bất kỳ ai cần một xác nhận xml trên PS3, có thể sử dụng (dựa trên chức năng wangzq của)

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

Tôi đã viết lại nó (Tôi biết habbit xấu), nhưng kịch bản bắt đầu bằng việc @Flatliner_DOA quá tốt để loại bỏ hoàn toàn.

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 
} 

Chức năng không còn đường ống thay thế cho việc sử dụng đường dẫn tệp nữa, đây không phải là biến chứng mà trường hợp sử dụng này không cần. Cảm thấy tự do để hack các trình xử lý bắt đầu/xử lý/kết thúc.

1

Tôi nhận ra đây là một câu hỏi cũ tuy nhiên tôi đã thử các câu trả lời được cung cấp và không thể khiến họ làm việc thành công trong Powershell.

Tôi đã tạo hàm sau sử dụng một số kỹ thuật được mô tả tại đây. Tôi đã tìm thấy nó rất đáng tin cậy.

Tôi đã phải xác thực tài liệu XML trước khi vào các thời điểm khác nhau tuy nhiên tôi luôn tìm thấy số dòng là 0. Có vẻ như XmlSchemaException.LineNumber sẽ chỉ khả dụng trong khi tải tài liệu.

Nếu bạn làm xác nhận sau đó sử dụng phương pháp Validate() trên một XmlDocument sau đó linenumber/LinePosition sẽ luôn là 0.

Thay vào đó bạn nên làm xác nhận khi đọc sử dụng một XmlReader và thêm một xử lý sự kiện xác nhận để một khối kịch bản .

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 
} 
Các vấn đề liên quan