Powershell:更快的xml.parentNode.RemoveChild版本
我正在尝试通过 powershell 从一个非常大的 XLF 文件(基本上是一个 XML 文件)中删除节点。结构(简化)始终如下:
<?xml version="1.0" encoding="utf-8"?>
<xliff>
<file>
<body>
<group>
<trans-unit>
<source>asd</source>
<target>asd</target>
</trans-unit>
<trans-unit>
<source> </source>
<target> </target>
</trans-unit>
<trans-unit>
<source>asd</source>
<target>asdf</target>
</trans-unit>
</group>
</body>
</file>
</xliff>
现在我想删除此文件中源和目标相等的所有节点。
这是我到目前为止所拥有的:
匹配功能:
function Match{
param(
$sourceNode,$targetNode
)
#do this because empty string as xml value is of type xmlElement and fails to compare
if ($sourceNode.innerText -eq " ") {
$source = $sourceNode.innerText
}
else {
$source = $sourceNode
}
if ($targetNode.innerText -eq " ") {
$target = $targetNode.innerText
}
else {
$target = $targetNode
}
return $source -eq $target
}
删除节点的代码:
$xml = [xml]((Get-Content $xmlPath -Encoding UTF8).Replace("trans-unit", "transunit"))
$xml.xliff.file.body.group.transunit | ForEach-Object {
if (Match $_.source $_.target) {
$_.parentNode.RemoveChild($_) | Out-Null
}
}
$xml = [xml]($xml.OuterXml.Replace("transunit", "trans-unit"))
$xml.Save($outPath)
这有效,但不幸的是它非常慢,因为文件大约有 300 000 个节点。重要的是,节点在保存时保留其属性以便以后进一步处理文件。
我无法完成的更快方法如下:
$xml = [xml]([System.IO.File]::ReadAllText($xmlPath).Replace("trans-unit", "transunit"))
$filteredNodes = $xml.xliff.file.body.group.transunit | Where-Object {
!(Match $_.source $_.target)
}
???
$xml = [xml]($xml.OuterXml.Replace("transunit", "trans-unit"))
$xml.Save($outPath)
获取包含目标和源不同的所有 XmlNode 的列表,但不幸的是我无法将此列表传递回 xml 文档
有没有更快的方法从文件中删除那些匹配的节点?
回答
你的方法有一些问题。
-
切勿使用
[System.IO.File]::ReadAllText($xmlPath)或Get-Content $xmlPath读取 XML 文件。这是错误的,因为它会杀死 XML 中内置的文件编码自动检测,而且它是浪费的,因为 XML 解析器完全能够自行读取文件 - 首先将其读入 PowerShell 变量没有任何意义。始终使用解析器直接加载 XML 文件:
$doc = New-Object xml $doc.Load($xmlPath) -
您应该使用 XPath 来选择要删除的候选对象:
$same = $doc.selectNodes('//trans-unit[source = target]')这些很容易迭代和删除:
foreach ($n in $same) { $n.parentNode.removeChild($n) }这与使用 .NET 的
XmlDocument. -
不要调用
string.Replace()XML 源代码。只是不要。
您可以使用 XSLT 从文档中剥离节点。这很有可能表现得更好:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="trans-unit[source = target]" />
</xsl:stylesheet>
PowerShell 中的用法是这样的:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes" encoding="utf-8" />
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="trans-unit[source = target]" />
</xsl:stylesheet>
直接使用 .NET 对象的警告是您需要提供完整路径。相对路径不起作用。您可以使用(Join-Path (Get-Location) 'filename.xml')在需要的地方创建完整路径。
THE END
二维码