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 文档

有没有更快的方法从文件中删除那些匹配的节点?

回答

你的方法有一些问题。

  1. 切勿使用[System.IO.File]::ReadAllText($xmlPath)Get-Content $xmlPath读取 XML 文件。这是错误的,因为它会杀死 XML 中内置的文件编码自动检测,而且它是浪费的,因为 XML 解析器完全能够自行读取文件 - 首先将其读入 PowerShell 变量没有任何意义。

    始终使用解析器直接加载 XML 文件:

    $doc = New-Object xml
    $doc.Load($xmlPath)
    
  2. 您应该使用 XPath 来选择要删除的候选对象:

    $same = $doc.selectNodes('//trans-unit[source = target]')
    

    这些很容易迭代和删除:

    foreach ($n in $same) {
        $n.parentNode.removeChild($n)
    }
    

    这与使用 .NET 的XmlDocument.

  3. 不要调用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')在需要的地方创建完整路径。


以上是Powershell:更快的xml.parentNode.RemoveChild版本的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>