[英]PowerShell XML find element that is an exact match
我正在将卸载数据导出到 XML,主要用于 Autodesk 产品。 而且 Autodesk 有复制东西的习惯,比如同一软件的多个版本,具有不同的 GUID,但不能并排安装,并且安装更新不会删除旧的 GUID。 我的 XML 的工作方式是,我将 GUID 抽象为卸载字符串中的一个变量,因此我可以拥有一个<UninstallProgram>
元素,其中包含查找和删除所有实例所需的数据。 但是当然我在注册表中找到了两次数据,所以我当前的代码创建了两个元素。 最终结果是我可以拥有这个元素两次。
<UninstallProgram id="Lighting Analysis for Revit 2023">
<Search>Lighting Analysis for Revit 2023</Search>
<Filter>UninstallString -like *AdODIS*</Filter>
<Resource>C:\ProgramData\Autodesk\ODIS\metadata</Resource>
<Executable>C:\Program Files\Autodesk\AdODIS\V1\Installer.exe</Executable>
<Arguments>-i uninstall --trigger_point system -m [Task~Resource]\[Task~GUID]\bundleManifest.xml -x [Task~Resource]\[Task~GUID]\SetupRes\manifest.xsd -q</Arguments>
</UninstallProgram>
我想知道的是,是否有一种简单的方法来获取一个已创建但尚未附加的元素变量,并搜索任何其他完全相同的元素,包括所有属性、子元素和元素文本? 我知道我可以搜索具有相同 ID 的元素,但是如果 Autodesk 做了一些奇怪的事情并且我以某种方式构建了具有相同 ID 但内容不同的第二个元素,我想要 append 以便我可以找到它并开始将我的代码寻址到找到 Autodesk 提供的新条件。 我不想花太多时间或代码。 我已经研究过迭代当前的 XML,将每个元素转换为字符串表示形式并将其与正在评估的元素的字符串表示形式进行比较,但这会在性能方面变得丑陋,因为当前的 XML 变得越来越大,并且我会做这个比较数百次。 而且这个问题很少出现,仅仅手动编辑 XML 并不是什么大问题。 理想情况下,我想要的是 XPath 的一部分,经过高度优化,允许像这样的条件
if ($xmlSelectSingleNode("NotMatch $newUninstallElement")){
[Void]$rootElement.AppendChild($newUninstallElement)
}
以下绝不是一个强大的解决方案,但它可能足以满足您的用例:
将.SelectNodes()
与基于id
属性的 XPath 查询一起使用,以查找与查找元素匹配的所有候选元素。
在候选元素中,通过.OuterXml
属性找到与查找元素的全部内容匹配的元素; 有关假设和限制,请参见下文。
# Sample document.
# Note that the two UninstallProgram elements differ by the <Search> element value only.
[xml] $xmlDoc = @'
<xml>
<UninstallProgram id="Lighting Analysis for Revit 2023">
<Search>Lighting Analysis for Revit 2023</Search>
<Filter>UninstallString -like *AdODIS*</Filter>
<Resource>C:\ProgramData\Autodesk\ODIS\metadata</Resource>
<Executable>C:\Program Files\Autodesk\AdODIS\V1\Installer.exe</Executable>
<Arguments>-i uninstall --trigger_point system -m [Task~Resource]\[Task~GUID]\bundleManifest.xml -x [Task~Resource]\[Task~GUID]\SetupRes\manifest.xsd -q</Arguments>
</UninstallProgram>
<UninstallProgram id="Lighting Analysis for Revit 2023">
<Search>DIFFERS</Search>
<Filter>UninstallString -like *AdODIS*</Filter>
<Resource>C:\ProgramData\Autodesk\ODIS\metadata</Resource>
<Executable>C:\Program Files\Autodesk\AdODIS\V1\Installer.exe</Executable>
<Arguments>-i uninstall --trigger_point system -m [Task~Resource]\[Task~GUID]\bundleManifest.xml -x [Task~Resource]\[Task~GUID]\SetupRes\manifest.xsd -q</Arguments>
</UninstallProgram>
</xml>
'@
# A sample element to look for in the document.
# Note: The assumption is that its .OuterXml property has no incidental whitespace,
# which is what using an [xml] cast does.
$elem = ([xml] '<UninstallProgram id="Lighting Analysis for Revit 2023">
<Search>Lighting Analysis for Revit 2023</Search><Filter>UninstallString -like *AdODIS*</Filter><Resource>C:\ProgramData\Autodesk\ODIS\metadata</Resource><Executable>C:\Program Files\Autodesk\AdODIS\V1\Installer.exe</Executable><Arguments>-i uninstall --trigger_point system -m [Task~Resource]\[Task~GUID]\bundleManifest.xml -x [Task~Resource]\[Task~GUID]\SetupRes\manifest.xsd -q</Arguments>
</UninstallProgram>').DocumentElement
# Find all elements with the same ID using an XPath query, then
# compare each matching element's .OuterXml values to that of the lookup element.
$xmlDoc.
SelectNodes(('//UninstallProgram[@id="{0}"]' -f $elem.id)).
Where({ $_.OuterXml -ceq $elem.OuterXml })
上面只找到第一个<UninstallProgram>
元素,因为 - 虽然两者都具有相同的id
属性,因此通过传递给.SelectNodes()
的 XPath 查询匹配 - 只有第一个的内容,如.OuterXml
属性值中所反映的那样,匹配查找元素的那个。
假设:
输入文档和要查找的元素都必须在删除附带的空格的情况下进行解析; 默认情况下,使用 PowerShell 中的[xml]
转换(将 XML 文本解析为System.Xml.XmlDocument
实例)。
目标元素的属性、子元素及其属性在输入文档和查找元素中的顺序必须相同。
如果涉及 XML 命名空间,则需要做更多工作。
考虑 XPath 的兄弟XSLT使用Muenchian 方法对节点进行重复数据删除,其中使用<xsl:key>
实现文档上的 hash 表以进行高效处理。 PowerShell 可以使用 .NET 的XslCompiledTransform Class运行 XSLT 1.0。 具体来说,下面的样式表运行身份转换以按原样复制文档,用点表示法索引<UninstallProgram>
的所有底层内容,并仅保留第一个唯一实例<UninstallProgram>
及其内容。
XSLT (另存为.xsl,一个特殊的.xml文件)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>
<xsl:key name="txt" match="UninstallProgram" use="." />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="UninstallProgram[generate-id() != generate-id(key('txt', .))]"/>
</xsl:stylesheet>
PowerShell
# Load the style sheet.
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$xslt.Load("C:\Path\To\style.xsl");
# Execute the transform and output the results to a file.
$xslt.Transform("C:\Path\To\input.xml", "C:\Path\To\output.xml");
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.