2009-05-05 2 views
22

В качестве части моего развития я хотел бы иметь возможность проверять ценность XML-файлов всей папки в отношении одного файла XSD. Функция PowerShell кажется хорошим кандидатом для этого, так как я могу просто передать список файлов так: dir * .xml | Validate-Xml -Schema. \ MySchema.xsdКак использовать PowerShell для проверки файлов XML на XSD?

Я рассмотрел вопрос о переносе кода C# из вопроса Validating an Xml against Referenced XSD in C#, но я не знаю, как добавлять обработчики в PowerShell.

+0

Почему именно вам нужен PowerShell только потому, что вы читаете список файлов из stdin? –

+2

Я хотел бы иметь возможность легко интегрировать его в автоматизированные скрипты сборки. Не хотел компилировать приложение только для этого. Сценарий PowerShell казался естественным подспорьем для такого рода вещей. –

ответ

11

Я написал функцию PowerShell, чтобы сделать это:

Использование:

реж * .xml | Тест-Xml -schema "\ MySchemaFile.xsd" -Namespace "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

+0

«$ reader» должен быть закрыт после цикла while. В противном случае вы не сможете редактировать файл до тех пор, пока не заработает Finalizer-saftey-net. –

+0

Это не работает: PS D: \ projects \ svcs> dir * .xml | Test-Xml Термин «Test-Xml» не распознается как имя командлета, функции, файла сценария или исполняемой программы. – Chloe

13

PowerShell Community Extensions имеет командлет Test-Xml. Единственным недостатком является то, что расширения не обновлялись некоторое время, но большинство из них работают на последней версии powershell (включая Test-Xml). Просто сделайте Get-Childitem и передайте список в foreach, вызвав Test-Xml для каждого.

+1

v1.2 расширений были выпущены для поддержки v2 PowerShell. Они все работают хорошо, поэтому я не уверен в недостатках. –

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 -validation> dir test.xml | Тест-XMLFILE test.xsd

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

Ваш ответ замечательный, короткий и эффективный :) вы просто пропустите '$ schemaReader.Dispose()', который вызывает файл схемы lock – Adassko

+0

Thanks; Я обновил свой ответ с обновленной версией, которая написана как функция, которая может быть включена в модуль и поддерживает конвейер. – wangzq

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

2

решение (Flatliner DOA) работает хорошо на pSV2, но не на сервере 2012 PSv3.

решение (wangzq) работает на PS2 и PS3 !!

кто нуждается в валидации XML на PS3, могут использовать эту функцию (на основе функции 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

я переписал его (я знаю плохо Habbit), но исходный сценарий по @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 
} 

Функция больше не трубопроводов в качестве альтернативы использованию файла-путь, это осложнение этого варианта использования не требуется. Не стесняйтесь взломать обработчики begin/process/end.

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 
} 
Смежные вопросы