1

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?

FalconRider
  • 75
  • 2
  • 9
  • the xml file is only read once in line 2. Executing your code in a loop rather than sequentially also will not give you any performance benefit. What exactly is your concern with the script? – Paul May 06 '22 at 14:47
  • understood, but is there any way to combine all of this and make the logic smaller instead of having it like what I have right now? – FalconRider May 06 '22 at 14:55

3 Answers3

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
1

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)
Paul
  • 5,524
  • 1
  • 22
  • 39
0

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)

Parfait
  • 104,375
  • 17
  • 94
  • 125