Как использовать PowerShell для проверки XML-файлов на XSD? - PullRequest
24 голосов
/ 05 мая 2009

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

Я рассмотрел возможность переноса кода C # из Проверка Xml против ссылочного XSD в C # , но я не знаю, как добавить обработчики в PowerShell.

Ответы [ 8 ]

14 голосов
/ 05 мая 2009

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

12 голосов
/ 05 мая 2009

Я написал функцию PowerShell для этого:

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

dir * .xml | Test-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"
}
}
10 голосов
/ 18 мая 2013

Хочу прокомментировать, что скрипт в текущем принятом ответе не проверяет ошибки о неправильных порядках элементов 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 \ lab-xml-validation> dir test.xml | Test-XmlFile test.xsd

System.Xml.Schema.XmlSchemaValidationException: The element 'address' has invalid child element 'name'.
...
3 голосов
/ 24 мая 2013

Я использую этот простой фрагмент, всегда работает, и вам не нужны сложные функции. В этом примере я загружаю конфигурационный 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
    })
2 голосов
/ 04 сентября 2013

Решение (Flatliner DOA) хорошо работает на PSv2, но не на Server 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
1 голос
/ 11 февраля 2015

Я понимаю, что это старый вопрос, однако я попробовал предоставленные ответы и не смог заставить их успешно работать в 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
}
1 голос
/ 08 февраля 2012

Я создал отдельный файл PowerShell, который может выполнять проверку XSD для файлов XML со ссылкой на встроенную схему. Работает действительно хорошо. Загрузка и инструкции доступны на https://knowledge.zomers.eu/PowerShell/Pages/How-to-validate-XML-against-an-XSD-schema-using-PowerShell.aspx

0 голосов
/ 22 января 2014

Я переписал это (я знаю плохой хаббит), но стартовый скрипт @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
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...