PowerShell递归解析XML转CSV遇无效字符转换错误求助
PowerShell递归解析XML转CSV时的无效字符错误处理
现有实现代码
第一段通用XML扁平化代码
function Flatten-Xml ([System.Xml.XmlNode]$Xml, [Parameter(DontShow)]$ParentName) { if (!$ParentName) { $Properties = [Ordered]@{} } $Xml |Get-Member -MemberType 'Property' |ForEach-Object { $Name = if ($ParentName) { "$ParentName.$($_.Name)" } else { $_.Name } $Value = $Xml.$($_.Name) if ($Value -is [System.Xml.XmlElement]) { Flatten-Xml $Value $Name } elseif ($Value -is [String]) { $Properties[$Name] = $Value } } if (!$ParentName) { [PSCustomObject]$Properties } } function UnifyProperties { $Names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) $InputCollected = @($Input) $InputCollected.ForEach({ foreach ($Name in $_.psobject.Properties.Name) { $Null = $Names.Add($Name) } }) $inputCollected | Select-Object @($Names) } $ListOfTasks = (Get-ChildItem -File -Path .\ -Recurse).fullname $Final = @() $ListOfTasks | foreach { $xml = [xml](get-content $_) $Final += Flatten-Xml $Xml } $Final | UnifyProperties | Export-Csv -Path .\01Test.csv -NoTypeInformation
第二段针对Windows任务计划XML的专用解析代码
[CmdletBinding()] param ( [Parameter(Mandatory = $true)] $PathToXML ) $ListOfTasks = (Get-ChildItem -File -Path $PathToXML -Recurse).fullname $ListOfTasks | foreach { $ModifiedTime = (gci -Path $_).LastWriteTimeUtc.toString('yyyy-MM-ddTHH:mm:ss')+':00z' $xmlFile = [xml](get-content "$_") $Date = $xmlFile.ChildNodes.RegistrationInfo.Date $Author = $xmlFile.ChildNodes.RegistrationInfo.Author $Description = $xmlFile.ChildNodes.RegistrationInfo.Description $URI = $xmlFile.ChildNodes.RegistrationInfo.URI $Principals = $xmlFile.ChildNodes.Principals.Principal.UserId $LogonType = $xmlFile.ChildNodes.Principals.Principal.LogonType $Enabled = $xmlFile.ChildNodes.Settings.Enabled $Action = $xmlFile.ChildNodes.Actions.Exec.Command $Arguments = $xmlFile.ChildNodes.Actions.Exec.Arguments $xmlFile.ChildNodes[1] | ForEach-Object { [PSCustomObject]@{ TaskFile_LastModifiedTime = $ModifiedTime Registration_Date = $Date Author = $Author Description = $Description Task_Name = $URI Principals_UserContext = $Principals LogonType = $LogonType Enabled = $Enabled Action_Arguments = $Action + " " + $Arguments #Arguments = $Arguments } } } | Export-Csv -Path .\Parsed_Tasks_XML.csv -NoTypeInformation
执行时的错误信息
Cannot convert value "System.Object[]" to type "System.Xml.XmlDocument". Error: "'', hexadecimal value 0x08, is an invalid character. Line 1, position 1." At C:\Users\Vikas\Desktop\0Sophos-P520\Research\Parse_TaskXMLs_Verbose.ps1:27 char:5 + $xml = [xml](get-content $_) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [], RuntimeException + FullyQualifiedErrorId : InvalidCastToXmlDocument
问题分析与解决方案
问题根源
错误提示的0x08是退格符,属于XML规范中禁止的控制字符。虽然Windows任务计划导出的XML通常规范,但部分文件可能因为导出异常、编辑残留等原因包含这类无效字符,且原代码没有错误捕获机制,无法定位具体出错文件。
解决步骤
1. 添加错误捕获,定位出错文件
修改文件处理逻辑,加入try/catch块,捕获错误并输出出错文件路径:
# 以第一段代码为例,修改foreach部分 $ListOfTasks | foreach { $filePath = $_ try { $xml = [xml](get-content $filePath) $Final += Flatten-Xml $Xml Write-Host "Successfully processed: $filePath" } catch { Write-Error "Failed to process file: $filePath`nError: $_" } }
2. 清理XML中的无效字符
在加载XML前,先读取文件内容并过滤掉XML禁止的控制字符,保留合法的制表符、换行、回车:
# 替换原代码中$xml = [xml](get-content $_)的部分 $content = Get-Content $filePath -Raw $cleanContent = $content -replace '[\x00-\x08\x0B\x0C\x0E-\x1F]','' $xml = [xml]$cleanContent
3. 优化专用解析代码的健壮性
针对任务计划XML的解析代码,增加节点存在性检查,避免因部分XML结构缺失导致额外错误:
# 示例:检查RegistrationInfo节点是否存在 if ($xmlFile.ChildNodes.RegistrationInfo) { $Date = $xmlFile.ChildNodes.RegistrationInfo.Date $Author = $xmlFile.ChildNodes.RegistrationInfo.Author $Description = $xmlFile.ChildNodes.RegistrationInfo.Description $URI = $xmlFile.ChildNodes.RegistrationInfo.URI } else { $Date = $Author = $Description = $URI = $null }
完整修改后的第一段代码示例
function Flatten-Xml ([System.Xml.XmlNode]$Xml, [Parameter(DontShow)]$ParentName) { if (!$ParentName) { $Properties = [Ordered]@{} } $Xml |Get-Member -MemberType 'Property' |ForEach-Object { $Name = if ($ParentName) { "$ParentName.$($_.Name)" } else { $_.Name } $Value = $Xml.$($_.Name) if ($Value -is [System.Xml.XmlElement]) { Flatten-Xml $Value $Name } elseif ($Value -is [String]) { $Properties[$Name] = $Value } } if (!$ParentName) { [PSCustomObject]$Properties } } function UnifyProperties { $Names = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase) $InputCollected = @($Input) $InputCollected.ForEach({ foreach ($Name in $_.psobject.Properties.Name) { $Null = $Names.Add($Name) } }) $inputCollected | Select-Object @($Names) } $ListOfTasks = (Get-ChildItem -File -Path .\ -Recurse).fullname $Final = @() $ListOfTasks | foreach { $filePath = $_ try { $content = Get-Content $filePath -Raw $cleanContent = $content -replace '[\x00-\x08\x0B\x0C\x0E-\x1F]','' $xml = [xml]$cleanContent $Final += Flatten-Xml $Xml Write-Host "Processed: $filePath" } catch { Write-Error "Failed to process: $filePath`nError detail: $_" } } $Final | UnifyProperties | Export-Csv -Path .\01Test.csv -NoTypeInformation
内容的提问来源于stack exchange,提问作者Origami
相关产品推荐
相关产品推荐

