'How to update XML with reading it only once with PowerShell?
Can someone please help me to do the following more efficiently? I am running this code 4 times for updating specific below protocols/file types. I would like to do it in a loop or other ways to make it much more efficient!
$XMLPATH = 'C:\DefaultAssociation.xml'
$xml = [xml](Get-Content $XMLPATH -raw)
#update .htm
$htm = ($xml.DefaultAssociations.Association | where-Object Identifier -eq '.htm')
$htm.SetAttribute('ProgId', "ChromeHTML")
$htm.SetAttribute('ApplicationName', "Google Chrome")
#update .html
$html = ($xml.DefaultAssociations.Association | where-Object Identifier -eq '.html')
$html.SetAttribute('ProgId', "ChromeHTML")
$html.SetAttribute('ApplicationName', "Google Chrome")
#update .https
$https = ($xml.DefaultAssociations.Association | Where-Object Identifier -eq 'https')
$https.SetAttribute('ProgId', "ChromeHTML")
$https.SetAttribute('ApplicationName', "Google Chrome")
#update http
$http = ($xml.DefaultAssociations.Association | Where-Object Identifier -eq 'http')
$http.SetAttribute('ProgId', "ChromeHTML")
$http.SetAttribute('ApplicationName', "Google Chrome")
$XML.SAVE($XMLPATH)
instead of running it multiple times, I need to make it an array or some sort of loop, so it doesn't read the XML file multiple times. How can this be done more efficiently?
Solution 1:[1]
Use PowerShell's -in
operator to equality-test against multiple values in a single operation, with any successful test short-circuiting further tests and returning $true
overall:
$htm = $xml.DefaultAssociations.Association |
Where-Object Identifier -in '.htm', '.html', 'https', 'http'
$htm.SetAttribute('ProgId', "ChromeHTML")
$htm.SetAttribute('ApplicationName', "Google Chrome")
A simple example to demonstrate how -in
works:
'Foo' -in 'bar', 'foo', 'baz'
yields $true
, and is the concise equivalent of:'Foo' -eq 'bar' -or 'Foo' -eq 'foo' -or 'Foo' -eq 'baz'
As all operators in PowerShell, -in
and -eq
are case-insensitive by default; for case-sensitivity, use their c
-prefixed variants, i.e., -cin
and -ceq
.
As an aside re $xml = [xml](Get-Content $XMLPATH -raw)
:
Loading XML documents this way isn't fully robust; use the following idiom instead:
($xml = [xml]::new()).Load((Convert-Path -LiteralPath $XMLPATH))
See this answer for background information.
Solution 2:[2]
To execute your statements in a loop you can do something like this: Although as mentioned in my comment it will not give you a performance benefit.
$XMLPATH = 'C:\DefaultAssociation.xml'
$xml = [xml](Get-Content $XMLPATH -raw)
$extensions = @(".htm",".html",".http",".https")
foreach($extension in $extensions){
$val = ($xml.DefaultAssociations.Association | where-Object Identifier -eq $extension)
$val.SetAttribute('ProgId', "ChromeHTML")
$val.SetAttribute('ApplicationName', "Google Chrome")
}
$XML.SAVE($XMLPATH)
Solution 3:[3]
Consider XSLT, the special purpose language designed to transform or style XML files. With this approach you read XML once and handle all changes. Plus you can add other needed changes. PowerShell can interface to the System.Xml.Xsl.XslCompiledTransform
XSLT (save as .xsl, a special .xml file)
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output version="1.0" encoding="UTF-8" indent="yes" method="xml"/>
<xsl:strip-space elements="*"/>
<!-- Identity Transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Adjust Identifier Attributes -->
<xsl:template match="Identifier[text()='htm' or text()='html' or text()='https' or text()='http']">
<xsl:copy>
<xsl:attribute name="ProgId">ChromeHTML</xsl:attribute>
<xsl:attribute name="ApplicationName">Google Chrome</xsl:attribute>
<xsl:apply-templates select="@*[name()!='ProgId' and name()!='ApplicationName']"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:transform>
PowerShell
$xml = "C:\DefaultAssociation.xml";
$xsl = "C:\Path\To\Script.xsl";
$output = "C:\Path\To\Output.xml";
$xslt = New-Object System.Xml.Xsl.XslCompiledTransform;
$settings = New-Object System.Xml.Xsl.XsltSettings($true, $true);
$resolver = New-Object System.Xml.XmlUrlResolver;
$xslt.Load($xsl, $settings, $resolver);
$xslt.Transform($xml, $output);
Online Demo (using dummy XML and same MS XslCompiledTransform class)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Paul |
Solution 3 | Parfait |