format-xml.ps1


<#PSScriptInfo
 
.VERSION 1.2
 
.GUID d3735921-156b-4cd7-883f-d3cbaa0c5f81
 
.AUTHOR James O'Neill
 
.COMPANYNAME Mobula Consulting Ltd
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI https://opensource.org/licenses/MS-PL
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.DESCRIPTION
    Takes XML Text or paths to XML files and produces nicely indented output
 
#>


<#
.SYNOPSIS
    Function to pretty-print XML
.DESCRIPTION
    Takes XML Text or paths to XML files and produces nicely indented output
.Parameter path
    Specifies the path to one or more XML files.
.Parameter InputObject
    The text to format, or a file object which refers to an XML file
    Has an alias "Text", and accepts input via the pipe.
.Example
    dir wl.* -recurse | Format-XML
    Finds one or matching files and outputs them as nicely indented XML
.Example
    Format-XML .\wl.wml
    Does the same but with a known path
.Example
    Invoke-WebRequest @parameters | format-xml
    Takes a set of pre-preared parameters which will return XML from a web service and formats the result.
#>


function Format-XML {
    Param   ( 
                [parameter(ValueFromPipelineByPropertyName=$true,ParameterSetName="Path",Mandatory=$true )][Alias('FullName')]
                $Path,
                [parameter(ValueFromPipeLine= $true,ParameterSetName="Pipe",Mandatory=$true, position=0)][Alias('Text')]
                $InputObject   
    )
  
    begin   {#to cope with multiple items being piped we will assemble blocks of XML or Lumps of text in the process block and output them in the end block
                [xml[]]$xmlblocks = @() 
                [String[]]$text   = @()     
    }
    process {
                #Catch file names; if text is not a file name assume it is going to be XML
                if    (($InputObject -is [string]) -and ($null -eq $Path) -and (Test-Path -Path $InputObject -ErrorAction SilentlyContinue)) {
                        $Path        =   $InputObject
                        $InputObject =   $null
                } 
                #Read XML File(s) specified via $path
                if     ($Path) { Resolve-Path -Path $Path -ErrorAction silentlycontinue | ForEach-Object { $xmlblocks += [xml](Get-Content -Path $_) } }
                #If input was a file object read XML from it
                elseif ($InputObject -is [System.IO.FileInfo]){
                        $doc = New-Object -TypeName System.Xml.XmlDataDocument
                        $doc.Load($InputObject)
                        $xmlblocks += $doc
                }
                #if input was already XML don't turn it back into text
                elseif ($InputObject -is [XML])               {$xmlblocks += $InputObject}
                #If input was text and not a path build up one big blok of text to convert at the end.
                else                                          {$text      += $InputObject}
    }
    end     { 
                #If the process block assembled something in $Text, convert it to XML
                if ($text) {$xmlblocks += [xml]$text }   

                #And now output the XML blocks - we need a couple of objects
                $sw     = New-Object -TypeName System.IO.StringWriter
                $writer = New-Object -TypeName System.Xml.XmlTextWriter -ArgumentList $sw -Property @{"Formatting" = [System.Xml.Formatting]::Indented } 
                foreach ($doc in $xmlblocks) {
                         $doc.WriteContentTo($writer) 
                         $sw.ToString() 
                }
    }
}