使用错误的行号验证 XML
Validate XML with line numbers in errors
我正在尝试使用行号获取 XML 验证错误,并且存在 [xml.xmlReaderSettings]
的 LineNumberOffset
属性 这一事实表明这是可能的。但我似乎无法找到如何在结果错误中启用行号或访问行号。 This 谈到在 C# 中用 LoadOptions.SetLineInfo;
做这件事但是当我尝试 $xmlReaderSettings.SetLineInfo = $true
.
时那不是有效的 属性
function readXMLFile ([string]$path) {
$readXMLFile = [psCustomObject]@{
xml = [xml.xmlDocument]::New()
error = $null
}
$fileStream = $null
$xmlreader = $null
$importFile = [xml.xmlDocument]::New()
$xmlReaderSettings = [xml.xmlReaderSettings]::New()
#$xmlReaderSettings.ignoreComments = $true
$xmlReaderSettings.closeInput = $true
$xmlReaderSettings.prohibitDtd = $false
$xmlReaderSettings.ValidationType = [System.Xml.ValidationType]::Schema
$xmlReaderSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings
$xmlReaderSettings.Schemas.Add($Null, $SchemaFile)
try {
$fileStream = [io.fileStream]::New($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$xmlreader = [xml.xmlreader]::Create($fileStream, $xmlReaderSettings)
$importFile.Load($xmlreader)
} catch {
$exceptionName = $_.exception.GetType().name
$exceptionMessage = $_.exception.message
switch ($exceptionName) {
MethodInvocationException {
if ($exceptionMessage -match ': "(?<string>.*)"$') {
$readXMLFile.error = "Error loading XML; $($matches['string'])"
} else {
$readXMLFile.error = "Error loading XML; $exceptionMessage"
}
}
Default {
$readXMLFile.error = "Error loading XML; $($exceptionName) - $exceptionMessage" # Or just the message?
}
}
} finally {
if ($xmlreader) {
$xmlreader.Dispose()
}
if ($readXMLFile.error) {
$readXMLFile.xml = $null
} else {
$readXMLFile.xml = $importFile
}
}
return $readXMLFile
}
编辑:我一直在研究的模式是
<?xml version = "1.0"?>
<xs:schema xmlns:xs = "http://www.w3.org/2001/XMLSchema">
<xs:element name = 'Definitions'>
<xs:complexType>
<xs:sequence>
<xs:element name = 'Sets' type = 'Sets' minOccurs = '0' maxOccurs = '1' />
<xs:element name = 'Packages' type = 'Packages' minOccurs = '0' maxOccurs = '1' />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name = 'Sets'>
<xs:sequence>
<xs:element name = "Set" type = 'Set' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'Set'>
<xs:sequence>
<xs:element name = 'Set' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Package' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Rollout' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Remove' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
<!--<xs:attribute name = 'id' type = 'xs:string'/>-->
</xs:complexType>
<xs:complexType name = 'Packages'>
<xs:sequence>
<xs:element name = 'Package' type = 'Package' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
<xs:attribute name = 'id' type = 'xs:string'/>
</xs:complexType>
<xs:complexType name = 'Package'>
<xs:sequence>
<xs:element name = 'Package' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Task' type='Task' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'Task'>
<xs:sequence>
<xs:element name = 'PreProcess' type='TaskPrePostProcess' minOccurs = '0' maxOccurs='1' />
<xs:element name = 'Process' type='TaskProcess' minOccurs = '1' maxOccurs='1' />
<xs:element name = 'PostProcess' type='TaskPrePostProcess' minOccurs = '0' maxOccurs='1' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'TaskPrePostProcess'>
<xs:sequence>
<xs:element name = 'Task' type='Task' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'TaskProcess'>
</xs:complexType>
</xs:schema>
一些简单的样本数据是
<?xml version="1.0" encoding="utf-8" ?>
<Definitions>
<Sets>
<Set id="Arch">
<Package>DTV_2017</Package>
</Set>
<Set id="Px_Arch">
<Package>RVT_2017</Package>
<Package>RVT_2018</Package>
</Set>
</Sets>
<Packages>
</Packages>
</Definitions>
编辑:有趣的是,当我删除验证并捕获格式错误的 XML 错误时,我确实得到了行号。它仅使用 XSD 文件进行验证,该文件产生的错误不是特别有用。
您正在与 PowerShell 的一些黑魔法作斗争,因为它有时会用自己的类型包装对象 :-(
。
如果您查看捕获的 System.Management.Automation.MethodInvocationException
,您会发现它有一个 InnerException
属性,其中包含 System.Xml.Schema.XmlSchemaValidationException
实例,XmlReader
实际上扔了,并且 that 已经得到了你想要的 LineNumber
和 LinePosition
属性。
然而,更简洁的方法是首先只捕获 XmlSchemaValidationException
异常,让其他一切抛出。这样,PowerShell 会为您提供原始异常而不是其包装器:
catch [System.Xml.Schema.XmlSchemaValidationException]
{
$ex = $_.Exception;
$type = $ex.GetType().FullName;
$lineNumber = $ex.LineNumber;
$linePosition = $ex.LinePosition;
$message = $ex.Message;
write-host "type = $type";
write-host "line = $lineNumber";
write-host "position = $linePosition";
write-host "message = $message";
...
}
输出:
type = System.Xml.Schema.XmlSchemaValidationException
line = 4
position = 14
message = The 'id' attribute is not declared.
顺便说一句,您可能还想从 $xmlReaderSettings.Schemas.Add($Null, $SchemaFile)
中捕获 return 值,否则它将从您的函数写入输出流并给出一些奇怪的结果...
$null = $xmlReaderSettings.Schemas.Add($Null, $SchemaFile)
我正在尝试使用行号获取 XML 验证错误,并且存在 [xml.xmlReaderSettings]
的 LineNumberOffset
属性 这一事实表明这是可能的。但我似乎无法找到如何在结果错误中启用行号或访问行号。 This 谈到在 C# 中用 LoadOptions.SetLineInfo;
做这件事但是当我尝试 $xmlReaderSettings.SetLineInfo = $true
.
function readXMLFile ([string]$path) {
$readXMLFile = [psCustomObject]@{
xml = [xml.xmlDocument]::New()
error = $null
}
$fileStream = $null
$xmlreader = $null
$importFile = [xml.xmlDocument]::New()
$xmlReaderSettings = [xml.xmlReaderSettings]::New()
#$xmlReaderSettings.ignoreComments = $true
$xmlReaderSettings.closeInput = $true
$xmlReaderSettings.prohibitDtd = $false
$xmlReaderSettings.ValidationType = [System.Xml.ValidationType]::Schema
$xmlReaderSettings.ValidationFlags = [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessInlineSchema -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation -bor
[System.Xml.Schema.XmlSchemaValidationFlags]::ReportValidationWarnings
$xmlReaderSettings.Schemas.Add($Null, $SchemaFile)
try {
$fileStream = [io.fileStream]::New($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
$xmlreader = [xml.xmlreader]::Create($fileStream, $xmlReaderSettings)
$importFile.Load($xmlreader)
} catch {
$exceptionName = $_.exception.GetType().name
$exceptionMessage = $_.exception.message
switch ($exceptionName) {
MethodInvocationException {
if ($exceptionMessage -match ': "(?<string>.*)"$') {
$readXMLFile.error = "Error loading XML; $($matches['string'])"
} else {
$readXMLFile.error = "Error loading XML; $exceptionMessage"
}
}
Default {
$readXMLFile.error = "Error loading XML; $($exceptionName) - $exceptionMessage" # Or just the message?
}
}
} finally {
if ($xmlreader) {
$xmlreader.Dispose()
}
if ($readXMLFile.error) {
$readXMLFile.xml = $null
} else {
$readXMLFile.xml = $importFile
}
}
return $readXMLFile
}
编辑:我一直在研究的模式是
<?xml version = "1.0"?>
<xs:schema xmlns:xs = "http://www.w3.org/2001/XMLSchema">
<xs:element name = 'Definitions'>
<xs:complexType>
<xs:sequence>
<xs:element name = 'Sets' type = 'Sets' minOccurs = '0' maxOccurs = '1' />
<xs:element name = 'Packages' type = 'Packages' minOccurs = '0' maxOccurs = '1' />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name = 'Sets'>
<xs:sequence>
<xs:element name = "Set" type = 'Set' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'Set'>
<xs:sequence>
<xs:element name = 'Set' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Package' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Rollout' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Remove' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
<!--<xs:attribute name = 'id' type = 'xs:string'/>-->
</xs:complexType>
<xs:complexType name = 'Packages'>
<xs:sequence>
<xs:element name = 'Package' type = 'Package' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
<xs:attribute name = 'id' type = 'xs:string'/>
</xs:complexType>
<xs:complexType name = 'Package'>
<xs:sequence>
<xs:element name = 'Package' type='xs:string' minOccurs = '0' maxOccurs='unbounded' />
<xs:element name = 'Task' type='Task' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'Task'>
<xs:sequence>
<xs:element name = 'PreProcess' type='TaskPrePostProcess' minOccurs = '0' maxOccurs='1' />
<xs:element name = 'Process' type='TaskProcess' minOccurs = '1' maxOccurs='1' />
<xs:element name = 'PostProcess' type='TaskPrePostProcess' minOccurs = '0' maxOccurs='1' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'TaskPrePostProcess'>
<xs:sequence>
<xs:element name = 'Task' type='Task' minOccurs = '0' maxOccurs='unbounded' />
</xs:sequence>
</xs:complexType>
<xs:complexType name = 'TaskProcess'>
</xs:complexType>
</xs:schema>
一些简单的样本数据是
<?xml version="1.0" encoding="utf-8" ?>
<Definitions>
<Sets>
<Set id="Arch">
<Package>DTV_2017</Package>
</Set>
<Set id="Px_Arch">
<Package>RVT_2017</Package>
<Package>RVT_2018</Package>
</Set>
</Sets>
<Packages>
</Packages>
</Definitions>
编辑:有趣的是,当我删除验证并捕获格式错误的 XML 错误时,我确实得到了行号。它仅使用 XSD 文件进行验证,该文件产生的错误不是特别有用。
您正在与 PowerShell 的一些黑魔法作斗争,因为它有时会用自己的类型包装对象 :-(
。
如果您查看捕获的 System.Management.Automation.MethodInvocationException
,您会发现它有一个 InnerException
属性,其中包含 System.Xml.Schema.XmlSchemaValidationException
实例,XmlReader
实际上扔了,并且 that 已经得到了你想要的 LineNumber
和 LinePosition
属性。
然而,更简洁的方法是首先只捕获 XmlSchemaValidationException
异常,让其他一切抛出。这样,PowerShell 会为您提供原始异常而不是其包装器:
catch [System.Xml.Schema.XmlSchemaValidationException]
{
$ex = $_.Exception;
$type = $ex.GetType().FullName;
$lineNumber = $ex.LineNumber;
$linePosition = $ex.LinePosition;
$message = $ex.Message;
write-host "type = $type";
write-host "line = $lineNumber";
write-host "position = $linePosition";
write-host "message = $message";
...
}
输出:
type = System.Xml.Schema.XmlSchemaValidationException
line = 4
position = 14
message = The 'id' attribute is not declared.
顺便说一句,您可能还想从 $xmlReaderSettings.Schemas.Add($Null, $SchemaFile)
中捕获 return 值,否则它将从您的函数写入输出流并给出一些奇怪的结果...
$null = $xmlReaderSettings.Schemas.Add($Null, $SchemaFile)