CMDLET Style XML Pretty Print: Format-Xml

That last attempt was kind of lame I admit.  Let’s try again with a more well rounded CMDLET style PowerShell function:
 
# ———————————————————————
# Author:    Keith Hill
# Desc:      CMDLET to pretty print XML.
# Usage:     This file contains a function-based CMDLET.  In order to use
#            it, you must dot source the file into your shell e.g.:
#            PoSH> . c:\bin\format-xml.ps1
# Date:      08/09/2006
# ———————————————————————
function Format-Xml {
    param([string[]]$Path)
       
    begin {
        function PrettyPrintXmlString([string]$xml) {
            $tr = new-object System.IO.StringReader($xml)
            $settings = new-object System.Xml.XmlReaderSettings
            $settings.CloseInput = $true
            $settings.IgnoreWhitespace = $true
            $reader = [System.Xml.XmlReader]::Create($tr, $settings)
           
            $sw = new-object System.IO.StringWriter
            $settings = new-object System.Xml.XmlWriterSettings
            $settings.CloseOutput = $true
            $settings.Indent = $true
            $writer = [System.Xml.XmlWriter]::Create($sw, $settings)
           
            while (!$reader.EOF) {
                $writer.WriteNode($reader, $false)
            }
            $writer.Flush()
           
            $result = $sw.ToString()
            $reader.Close()
            $writer.Close()
            $result
        }
       
        function PrettyPrintXmlFile($path) {
            $rpath = resolve-path $path
            $contents = gc $rpath
            $contents = [string]::join([environment]::newline, $contents)
            PrettyPrintXmlString $contents
        }
   
        function Usage() {
            ""
            "USAGE"
            "    Format-Xml -Path <pathToXmlFile>"
            ""
            "SYNOPSIS"
            "    Formats the XML into a nicely indented form (ie pretty printed)."
            "    Outputs one <string> object for each XML file."
            ""
            "PARAMETERS"
            "    -Path <string[]>"
            "        Specifies path to one or more XML files to format with indentation."
            "        Pipeline input is bound to this parameter."
            ""
            "EXAMPLES"
            "    Format-Xml -Path foo.xml"
            "    Format-Xml foo.xml"
            "    gci *.xml | Format-Xml" 
            "    [xml]`"<doc>…</doc>`" | Format-Xml"
            ""
        }
        if (($args[0] -eq "-?") -or ($args[0] -eq "-help")) {
          Usage
        }
    }
   
    process {
        if ($_) {
          if ($_ -is [xml]) {
            PrettyPrintXmlString $_.get_OuterXml()
          }
          elseif ($_ -is [IO.FileInfo]) {
            PrettyPrintXmlFile $_.FullName
          }
          elseif ($_ -is [string]) {
            if (test-path -type Leaf $_) {
                PrettyPrintXmlFile $_
            }
            else {
                PrettyPrintXmlString $_
            }
          }
          else {
            throw "Pipeline input type must be one of: [xml], [string] or [IO.FileInfo]"
          }
        }
    }
     
    end {
        if ($Path) {
          foreach ($aPath in $Path) {
            PrettyPrintXmlFile $aPath
          }
        }
    }
}
Advertisements
This entry was posted in PowerShell. Bookmark the permalink.

7 Responses to CMDLET Style XML Pretty Print: Format-Xml

  1. Sung says:

    Ah, now that looks a lot more "elegant" compared to the one I tried.
    Giving that "PrettyPrintXmlFile" a small responsibility really seems great… so that you can only call that function through ForEach-Object…
     
    I wonder how long until I reach the point to make the code look as clear as one you just wrote 😉

  2. Max says:

    It\’s so long!

  3. Keith says:

    It is a bit long.  I think this would make a great little cmdlet and in fact I have implemented this as a cmdlet in the PowerShell Community Extensions projet (see the related blog posts).

  4. Unknown says:

    Hi Keith,I can\’t seem to get your Format-Xml script to work at all on my Powershell….. it doesn\’t even complain about anything – it just doesn\’t do anything, either. Even calling it with ".\\Format-Xml.ps1 -?" doesn\’t do anything -the "Usage" explanation is not displayed. And I have "set-executionpolicy unrestricted" – still doesn\’t do anything 😦 What am I missing??Thanks!Marc

  5. Keith says:

    Marc, I\’m guessing you copied the function above to a file and then tried to execute the function.  You need to dot-source the file and then you can execute the function e.g.
     
    PS> . c:\\temp\\LibraryFormatXml.ps1
     
    PS> format-xml c:\\temp\\foo.xml

  6. Baris says:

    I think it is a great script 🙂

    Though it format the xml as “utf-16”, which is the default string encoding in .NET.
    To avoid this, I have modified the script a bit: (only the function PrettyPrintXmlString())

    function PrettyPrintXmlString([string]$xml) {
    $tr = new-object System.IO.StringReader($xml)
    $settings = new-object System.Xml.XmlReaderSettings
    $settings.CloseInput = $true
    $settings.IgnoreWhitespace = $true
    $reader = [System.Xml.XmlReader]::Create($tr, $settings)

    #create xml using memory stream to avoid utf-16 encoding
    #using stringwriter will set the encoding to utf-16, because the strings in .net are unicode
    $memoryStream = New-Object System.IO.MemoryStream
    $xmlWriterSettings = New-Object System.Xml.XmlWriterSettings
    $xmlWriterSettings.Encoding = New-Object System.Text.UTF8Encoding($false)
    $xmlWriterSettings.Indent = $true
    $xmlWriterSettings.CloseOutput = $true

    #create xml write and write nodes
    $xmlWriter = [System.Xml.XmlWriter]::Create($memoryStream, $xmlWriterSettings)
    while (!$reader.EOF) {
    $xmlWriter.WriteNode($reader, $false)
    }
    $xmlWriter.Flush()

    $result = [System.Text.Encoding]::UTF8.GetString($memoryStream.ToArray())

    $reader.Close()
    $xmlWriter.Close()
    $result
    }

    The function gets longer, but why do people complain about it, I don’t understand 🙂

  7. Pingback: MartijnBrant.net » Blog Archive » Reading / parsing / writing .ini files in Powershell using XML

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s