You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell合并XML按VATRegistrationNumber去重并保留空值条目

10万级条目XML低内存合并方案(适配Windows每日定时运行)

需求规则

两个结构完全一致的XML文件,单文件条目量均超10万,需满足以下合并要求:

  • 以File1为基准,输出文件完整保留File1全部条目
  • File2中仅VATRegistrationNumber字段值未在File1出现过的条目,才会被追加到输出文件
  • 所有VATRegistrationNumber为空的条目不受去重限制,无论是否重复全部保留
  • 方案需低内存占用,支持通过Windows任务计划程序每日定时运行,可选用XSLT、PowerShell、批处理实现

原有实现问题

现有两段PowerShell代码可实现基础合并去重,但存在两个核心缺陷:

  1. 全量合并生成中间文件代码:
[xml]$File1 = Get-Content file1.xml
[xml]$File2 = Get-Content file2.xml
ForEach ($XmlNode in $File2.DocumentElement.ChildNodes){$File1.DocumentElement.AppendChild($File1.ImportNode($XmlNode, $true))}
$File1.Save('file1and2combined.xml')
  1. 合并后去重代码:
[xml]$myfile = [xml](Get-content "file1and2combined.xml")
$nodesToRemove = $myfile.suppliers.childnodes | Group-Object VATRegistrationNumber |
    Foreach-Object { $_.Group | Select-Object -Skip 1 }
    
$nodesToRemove | Foreach-Object {
    $myfile.suppliers.RemoveChild($_)
}
#save XML
$myfile.Save("file1and2cleaned_result.xml")

缺陷说明

  • 去重逻辑错误:分组去重时会把VATRegistrationNumber为空的重复条目一并删除,不符合空值条目全保留的规则
  • 内存占用过高:采用全量DOM加载方式处理,10万级条目场景下内存开销通常超过1GB,还会额外生成中间文件,不适合长期定时稳定运行

优化后PowerShell实现(流式低内存版本)

采用XmlReader/XmlWriter做流式读写,全程不加载全量XML到内存,仅用哈希表存储File1中出现过的非空VAT号(查询时间复杂度O(1)),内存占用可降低90%以上,同时跳过中间文件生成步骤,处理速度更快。
逻辑修正点:单独对空VAT号条目做放行处理,自动过滤File2中与File1非空VAT号重复的条目,同时自动拦截File2内部的非空VAT重复条目

# 配置文件路径,可根据实际存放位置修改
$file1Path = "file1.xml"
$file2Path = "file2.xml"
$outputPath = "merged_final_result.xml"

# 初始化哈希表存储已存在的非空VAT号
$existingVatSet = @{}

# 初始化XML流式写入器
$writeSetting = New-Object System.Xml.XmlWriterSettings
$writeSetting.Indent = $true
$writeSetting.Encoding = [System.Text.Encoding]::UTF8
$xmlWriter = [System.Xml.XmlWriter]::Create($outputPath, $writeSetting)
$xmlWriter.WriteStartDocument()

# 流式读取File1,写入根节点
$reader1 = [System.Xml.XmlReader]::Create($file1Path)
while ($reader1.Read()) {
    if ($reader1.NodeType -eq [System.Xml.XmlNodeType]::Element -and $reader1.Name -eq "suppliers") {
        $xmlWriter.WriteStartElement("suppliers")
        # 复制根节点所有属性
        if ($reader1.HasAttributes) {
            while ($reader1.MoveToNextAttribute()) {
                $xmlWriter.WriteAttributeString($reader1.Name, $reader1.Value)
            }
            $reader1.MoveToElement()
        }
        break
    }
}

# 逐行读取File1的供应商条目,写入输出并收集非空VAT号
while ($reader1.Read()) {
    if ($reader1.NodeType -eq [System.Xml.XmlNodeType]::Element -and $reader1.Name -eq "supplier") {
        $subReader = $reader1.ReadSubtree()
        $tempDoc = New-Object System.Xml.XmlDocument
        $tempDoc.Load($subReader)
        $currentNode = $tempDoc.DocumentElement
        # 提取当前条目的VAT号
        $currentVat = $currentNode.SelectSingleNode("VATRegistrationNumber")?.InnerText.Trim()
        if (-not [string]::IsNullOrEmpty($currentVat)) {
            $existingVatSet[$currentVat] = $true
        }
        # 直接写入条目到输出文件
        $xmlWriter.WriteNode($tempDoc.CreateNavigator().ReadSubtree(), $false)
        $subReader.Close()
    }
}
$reader1.Close()

# 流式读取File2,按规则筛选条目写入
$reader2 = [System.Xml.XmlReader]::Create($file2Path)
while ($reader2.Read()) {
    if ($reader2.NodeType -eq [System.Xml.XmlNodeType]::Element -and $reader2.Name -eq "suppliers") {
        break
    }
}
while ($reader2.Read()) {
    if ($reader2.NodeType -eq [System.Xml.XmlNodeType]::Element -and $reader2.Name -eq "supplier") {
        $subReader = $reader2.ReadSubtree()
        $tempDoc = New-Object System.Xml.XmlDocument
        $tempDoc.Load($subReader)
        $currentNode = $tempDoc.DocumentElement
        $currentVat = $currentNode.SelectSingleNode("VATRegistrationNumber")?.InnerText.Trim()
        
        $keepNode = $false
        # 空VAT号直接保留
        if ([string]::IsNullOrEmpty($currentVat)) {
            $keepNode = $true
        }
        # 非空VAT号未在已有集合中则保留,同时加入集合避免File2内部重复
        elseif (-not $existingVatSet.ContainsKey($currentVat)) {
            $keepNode = $true
            $existingVatSet[$currentVat] = $true
        }

        if ($keepNode) {
            $xmlWriter.WriteNode($tempDoc.CreateNavigator().ReadSubtree(), $false)
        }
        $subReader.Close()
    }
}
$reader2.Close()

# 写入文档结束标签,释放资源
$xmlWriter.WriteEndElement()
$xmlWriter.WriteEndDocument()
$xmlWriter.Close()

Windows任务计划部署步骤

  • 将上述代码保存为XmlMerge.ps1,根据实际文件路径修改脚本内的路径配置
  • 打开Windows任务计划程序,新建任务,触发器设置为每日需要运行的时间点
  • 操作项选择「启动程序」,程序路径填写powershell.exe,参数填写-ExecutionPolicy Bypass -File "脚本存放的完整绝对路径\XmlMerge.ps1"
  • 可根据需要勾选「不管用户是否登录都要运行」,配置对应目录的读写权限即可

内容的提问来源于stack exchange,提问作者Jana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 20:15:44