【问题标题】:Parsing large XML files into PowerShell objects将大型 XML 文件解析为 PowerShell 对象
【发布时间】:2012-07-14 13:59:07
【问题描述】:

我是一名 PowerShell 和 XPath 初学者,努力高效地解析一些 XML 并构建一组对象以供进一步处理(例如 CSV 输出、SQL Server 加载)。下面包含一个 XML 示例以及我当前使用的代码 sn-p。在此模式中,每个 object-array 代表所需输出中的单行。我正在解析MetaData 子项以获取列的正确名称,然后构建一个 PSObject 集合,其中数组中的每个对象都代表一行。 MetaData 信息用于查找列名(PSObject 属性)。

这适用于大约 10K 行的文件,但在运行超过 500K 行的最大文件时会严重陷入困境。在这些情况下,每行大约需要 3-4 秒来处理。在 500K 行时,运行时间很长。我可以使用 XPath 或 PS 变量赋值来加快速度吗?

当务之急是将此 XML 转换为 CSV(目前通过 export-csv 执行),但我更希望脚本的这一部分生成对象集合,因为我接下来将要加载将此数据导入 SQL Server 实例或进行其他处理。

感谢您的帮助!

大卫

示例 XML

<Report>
<Data>
<Columns>
<MetaData>
<Index>0</Index>
<Name>Column1</Name>
<Index>1</Index>
<Name>Column2</Name>
<Index>2</Index>
<Name>Column3</Name>
</MetaData>
</Columns>
<Rows>
<object-array>
<string>column1 value</string>
<int>column2 value</string>
<string>column3 value</string>
</object-array>
</Rows>
</Data>
</Report>

示例代码

#extract the column headers
[string[]]$ColumnHeaders = @()
$obj.SelectNodes("/Report/Data/Columns/MetaData") |% {$ColumnHeaders += $_.name}

$collection = @()
$rowint = 0
$rowcount = $obj.Report.Data.Rows."object-array".count

#unwind the rows
do {
    $hash=@{}

    #loop through each element in the row parent element and add it to the hash
    $columnint = 0
    $columncount = (Select-Xml -xPath "Report/Data/Rows/object-array[$rowint]/node()" $obj).count
        do {
            $hash.Add($columnheaders[$columnint], (Select-Xml -xPath "Report/Data/Rows/object-array[$rowint]/descendant::text()[$columnint]" $obj).Node.Value)
            $columnint++
        } while ($columnint -lt $columncount)


    $thisrow = New-Object PSObject -Property $hash 

    #add this new row to the collection 
    $collection += $thisrow 
    $rowint++
} while ($rowint -lt $rowcount)

【问题讨论】:

    标签: powershell xml-parsing


    【解决方案1】:

    您无需在每次迭代中重新创建 ColumnHeaders 即可获取 MetaData 名称:

    $ColumnHeaders = $obj.Report.Data.Columns.MetaData.Name
    

    同样适用于 $collection。您的代码的最终结果如何?

    更新:试试这个

    [xml]$obj = Get-Content test.xml
    
    $data = $obj.Report.Data
    
    $pso = New-Object PSObject
    $pso | Add-Member NoteProperty -Name $data.Columns.MetaData.Name[0] -Value $data.Rows.'object-array'.string[0]
    $pso | Add-Member NoteProperty -Name $data.Columns.MetaData.Name[1] -Value $data.Rows.'object-array'.int
    $pso | Add-Member NoteProperty -Name $data.Columns.MetaData.Name[2] -Value $data.Rows.'object-array'.string[1] -PassThru
    

    【讨论】:

    • $obj.Report.Data.Columns.MetaData.Name 什么都不返回,而$obj.Report.Data.Columns.MetaData |gm 显示我正在返回 XMLElements,它具有关联的 Name 属性。最终结果是对象数组$collection,然后可以通过管道传输到 export-csv、ft 或其他 PS 处理。
    • 你能包含一个示例输出吗?
    • 输出是 PSObject 的集合,具有对应于列标题的属性和对应于行的值(对象数组)。在示例 XML 中,结果将是具有以下属性/值对的单个对象:Column1="Column1 Value"、Column2="Column2 Value"、Column3="Column3 Value"。在处理实时数据的情况下,将有一个包含 10,000-500,000 个这些对象的 PS 数组,然后可以将其提取到 export-csv(用于 SQL Server 加载的数据集),或直接在 PS 中进一步处理。
    • 澄清一下,每个&lt;object-array&gt; 组都相当于电子表格中的一行。我正在尝试解析 MetaData 组以获取正确的列标题(仅在脚本开始时发生一次),然后使用在所有 object-array 元素中找到的数据填充所有行(PSObjects),其中在给定的 XML 文件中有几千个对象数组元素。
    • 更新了我的答案,试试看
    猜你喜欢
    • 2011-07-28
    • 1970-01-01
    • 2011-05-09
    • 1970-01-01
    • 2017-10-30
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多