Archive

Archive for October, 2010

Powershell, XML and XSLT as one big happy family

I was scanning through my blog feeds today when I stumbled upon this video tutorial, showing how to use Visual Studio for XSLT development. I was impressed by the good support for XSLT and specifically by the great debugging capabilities. I wanted to try it out so I needed a problem that could be solved using XSLT. One thing that came to my mind was to transform the xml output from the ConvertTo-XML powershell commandlet. Given the following script:

$o = new-object psobject -property @{ Name=‘Peter’; Age=12 }
$o2 = new-object psobject -property @{ Name=‘Samuel’; Age=20 }
$o, $o2 | ConvertTo-Xml -As String

the resulting xml will be:

<?xml version="1.0"?>

<Objects>

  <Object Type="System.Management.Automation.PSCustomObject">

    <Property Name="Name" Type="System.String">Peter</Property>

    <Property Name="Age" Type="System.Int32">12</Property>

  </Object>

  <Object Type="System.Management.Automation.PSCustomObject">

    <Property Name="Name" Type="System.String">Samuel</Property>

    <Property Name="Age" Type="System.Int32">20</Property>

  </Object>

</Objects>

This xml document is not very convenient for powershell scripting. For example, accessing the age of Samuel would require the following line, assume that the xml document was stored in $data:

$data.Objects.Object[1].Property[1].‘#text’

Not that obvious, indeed. Using Visual Studio I was able to come up with the following XLST transform:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"

>

  <xsl:output method="xml" indent="yes"/>

 

  <xsl:template match="@* | node()">

    <xsl:copy>

      <xsl:apply-templates select="@* | node()"/>

    </xsl:copy>

  </xsl:template>

 

  <!– ref: http://www.dpawson.co.uk/xsl/sect2/N7240.html –>

  <xsl:template name="substring-after-last">

    <xsl:param name="string" />

    <xsl:param name="delimiter" />

    <xsl:choose>

      <xsl:when test="contains($string, $delimiter)">

        <xsl:call-template name="substring-after-last">

          <xsl:with-param name="string" select="substring-after($string, $delimiter)" />

          <xsl:with-param name="delimiter" select="$delimiter" />

        </xsl:call-template>

      </xsl:when>

      <xsl:otherwise>

        <xsl:value-of select="$string" />

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>

 

  <xsl:template match="Object">

    <xsl:variable name="shortTypeName">

      <xsl:call-template name="substring-after-last">

        <xsl:with-param name="string" select="@Type" />

        <xsl:with-param name="delimiter" select="‘.’" />

      </xsl:call-template>

    </xsl:variable>

    <xsl:element name="{$shortTypeName}">

      <xsl:apply-templates select="node()"/>

    </xsl:element>

  </xsl:template>

 

  <xsl:template match="Property">

    <xsl:element name="{@Name}">

      <xsl:value-of select="./text()"/>

    </xsl:element>

  </xsl:template>

 

</xsl:stylesheet>

 

When applied to the xml document above it produces the following:

<?xml version="1.0" encoding="utf-8"?>

<Objects>

  <PSCustomObject>

    <Name>Peter</Name>

    <Age>12</Age>

  </PSCustomObject>

  <PSCustomObject>

    <Name>Samuel</Name>

    <Age>20</Age>

  </PSCustomObject>

</Objects>

This xml document allows a much more natural navigation. Samuels age can now be retrieved using:

$data.Objects.PSCustomObject[1].Age

Advertisements
Categories: Uncategorized

Powershell: Code snippet of the day

Powershell supports background jobs. Related commandlets are:

  • Start-Job
  • Stop-Job
  • Wait-Job
  • Get-Job
  • Recieve-Job

which by the way can be listed directly by “get-command *job*”. Todays code snippet uses a background job to simulate a growing log file:

# simulate incremental log file growth
start-job -initializationScript {
  new-item -force -type file c:\temp\logfile.txt
} -scriptblock {
    $count = 0
    while ($count -lt 100)
    {
        add-content c:\temp\logfile.txt "line $count"
        [System.Threading.Thread]::Sleep(1000)
        $count ++
    }
}

We can use the get-content command to incrementally update (tail) the result:

# tail file
get-content c:\temp\logfile.txtwait

Note: For some reason the “Tail-File –Wait” command from Powershell community extensions fails. It seems to lock the file, preventing the background job from updating it. Strange.

Categories: Uncategorized

Code contemplation moved to wordpress

Microsoft is joining up with wordpress.

Categories: Uncategorized

Powershell: Iterate lines in a multiline string

October 16, 2010 5 comments

When applying the get-content command to a text file, it will produce a separate string object for each line, making it easy to process the lines individually:

Get-Content “myfile.txt” | ForEach-Object {
# process line
}

When the text is specified within a script, for example using the here string syntax, only one string is produced. Thus the following is invalid:

@”
Line 1
Line 2
Line 3
“@
| ForEach-Object {
# process line not possible – the entire multiline string is passed
}

In this case the split-string commandlet comes to rescue:

@”
Line 1
Line 2
Line 3
“@
| Split-String -separator “`r`n” | ForEach-Object {
# process line
}

Categories: Uncategorized