spacer PowerShell Code Repository

Xml Module 6.5 by Joel Bennett 7 months ago (modification of post by Joel Bennett view diff)
View followups from Jaykul | diff | embed code: <script type="text/javascript" src="/img/spacer.gif"> download | new post

A complete set of XML functionality (especially if you don’t have PSCX), including reading and writing xml files (import-xml, export-xml), selecting (via xpath), updating, transforming and creating new xml documents.

In particular:

A DSL for creating XML documents.

Convert-Xml which supports parameters so you can use XSLT which require parameters

Select-XML which leverages Remove-XmlNamespace to simplify simple xml queries by allowing you to leave out the namespaces. It is important to note that this means that the returned results will not have namespaces in them, even if the input XML did.

ALSO NOTE: only raw XmlNodes are returned from Select-Xml, so the output isn’t compatible with the built in Select-Xml — instead, it’s equivalent to using it the way I usually do: Select-Xml ... | Select-Object -Expand Node

Update-Xml which allows you to append, insert, remove, and replace values in XML Documents

Remove-XmlElement which can remove nodes or attributes by namespace to clean-up designer-generated XML

In this Version: I added Remove-XmlElement, added a Parameters parameter to New-XmlDocument, and fixed a bug in New-XElement which prevented using attribute values with dashes in them.

  1. #requires -version 2.0
  2.  
  3. # Improves over the built-in Select-XML by leveraging Remove-XmlNamespace http`://poshcode.org/1492
  4. # to provide a -RemoveNamespace parameter -- if it's supplied, all of the namespace declarations
  5. # and prefixes are removed from all XML nodes (by an XSL transform) before searching.
  6. # IMPORTANT: returned results *will not* have namespaces in them, even if the input XML did.
  7.  
  8. # Also, only raw XmlNodes are returned from this function, so the output isn't completely compatible
  9. # with the built in Select-Xml. It's equivalent to using Select-Xml ... | Select-Object -Expand Node
  10.  
  11. # Version History:
  12. # Select-Xml 2.0 This was the first script version I wrote.
  13. #                it didn't function identically to the built-in Select-Xml with regards to parameter parsing
  14. # Select-Xml 2.1 Matched the built-in Select-Xml parameter sets, it's now a drop-in replacement
  15. #                BUT only if you were using the original with: Select-Xml ... | Select-Object -Expand Node
  16. # Select-Xml 2.2 Fixes a bug in the -Content parameterset where -RemoveNamespace was *presumed*
  17. # Version    3.0 Added New-XDocument and associated generation functions for my XML DSL
  18. # Version    3.1 Fixed a really ugly bug in New-XDocument in 3.0 which I should not have released
  19. # Version    4.0 Never content to leave well enough alone, I've completely reworked New-XDocument
  20. # Version    4.1 Tweaked namespaces again so they don't cascade down when they shouldn't. Got rid of the unnecessary stack.
  21. # Version    4.2 Tightened xml: only cmdlet, function, and external scripts, with "-" in their names are exempted from being converted into xml tags.
  22. #                Fixed some alias error messages caused when PSCX is already loaded (we overwrite their aliases for cvxml and fxml)
  23. # Version    4.3 Added a Path parameter set to Format-Xml so you can specify xml files for prety printing
  24. # Version    4.5 Fixed possible [Array]::Reverse call on a non-array in New-XElement (used by New-XDocument)
  25. #                Work around possible variable slipping on null values by:
  26. #                1) allowing -param:$value syntax (which doesn't fail when $value is null)
  27. #                2) testing for -name syntax on the value and using it as an attribute instead
  28. # Version    4.6 Added -Arguments to Convert-Xml so that you can pass arguments to XSLT transforms!
  29. #                Note: when using strings for xslt, make sure you single quote them or escape the $ signs.
  30. # Version    4.7 Fixed a typo in the namespace parameter of Select-Xml
  31. # Version    4.8 Fixed up some uses of Select-Xml -RemoveNamespace
  32. # Version    5.0 Added Update-Xml to allow setting xml attributes or node content
  33. # Version    6.0 Major cleanup, breaking changes.
  34. #       - Added Get-XmlContent and Set-XmlContent for loading/saving XML from files or strings
  35. #       - Removed Path and Content parameters from the other functions (it greatly simplifies thost functions, and makes the whole thing more maintainable)
  36. #       - Updated Update-Xml to support adding nodes "before" and "after" other nodes, and to support "remove"ing nodes
  37. # Version    6.1 Update for PowerShell 3.0
  38. # Version    6.2 Minor tweak in exception handling for CliXml
  39. # Version    6.3 Added Remove-XmlElement to allow removing nodes or attributes
  40. #                This is something I specifically needed to remove "ignorable" namespaces
  41. #                Specifically, the ones created by the Visual Studio Workflow designer (and perhaps other visual designers like Blend)
  42. #                Which I don't want to check into source control, because it makes diffing nearly impossible
  43. # Version    6.4 Fixed a bug on New-XElement for Rudy Shockaert (nice bug report, thanks!)
  44. # Version    6.5 Added -Parameters @{} parameter to New-XDocument to allow local variables to be passed into the module scope. *grumble*
  45.  
  46. function Add-Accelerator {
  47. <#
  48.    .Synopsis
  49.       Add a type accelerator to the current session
  50.    .Description
  51.       The Add-Accelerator function allows you to add a simple type accelerator (like [regex]) for a longer type (like [System.Text.RegularExpressions.Regex]).
  52.    .Example
  53.       Add-Accelerator list System.Collections.Generic.List``1
  54.       $list = New-Object list[string]
  55.      
  56.       Creates an accelerator for the generic List[T] collection type, and then creates a list of strings.
  57.    .Example
  58.       Add-Accelerator "List T", "GList" System.Collections.Generic.List``1
  59.       $list = New-Object "list t[string]"
  60.      
  61.       Creates two accelerators for the Generic List[T] collection type.
  62.    .Parameter Accelerator
  63.       The short form accelerator should be just the name you want to use (without square brackets).
  64.    .Parameter Type
  65.       The type you want the accelerator to accelerate (without square brackets)
  66.    .Notes
  67.       When specifying multiple values for a parameter, use commas to separate the values.
  68.       For example, "-Accelerator string, regex".
  69.      
  70.       PowerShell requires arguments that are "types" to NOT have the square bracket type notation, because of the way the parsing engine works.  You can either just type in the type as System.Int64, or you can put parentheses around it to help the parser out: ([System.Int64])
  71.  
  72.       Also see the help for Get-Accelerator and Remove-Accelerator
  73.    .Link
  74.       huddledmasses.org/powershell-2-ctp3-custom-accelerators-finally/
  75. #>
  76. [CmdletBinding()]
  77. param(
  78.    [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)]
  79.    [Alias("Key","Name")]
  80.    [string[]]$Accelerator
  81. ,
  82.    [Parameter(Position=1,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  83.    [Alias("Value","FullName")]
  84.    [type]$Type
  85. )
  86. process {
  87.    # add a user-defined accelerator  
  88.    foreach($a in $Accelerator) {
  89.       if($xlr8r::AddReplace) {
  90.          $xlr8r::AddReplace( $a, $Type)
  91.       } else {
  92.          $null = $xlr8r::Remove( $a )
  93.          $xlr8r::Add( $a, $Type)
  94.       }
  95.       trap [System.Management.Automation.MethodInvocationException] {
  96.          if($xlr8r::get.keys -contains $a) {
  97.             if($xlr8r::get[$a] -ne $Type) {
  98.                Write-Error "Cannot add accelerator [$a] for [$($Type.FullName)]`n                  [$a] is already defined as [$($xlr8r::get[$a].FullName)]"
  99.             }
  100.             Continue;
  101.          }
  102.          throw
  103.       }
  104.    }
  105. }
  106. }
  107.  
  108. &{
  109. $local:xlr8r = [psobject].assembly.gettype("System.Management.Automation.TypeAccelerators")
  110. $local:xlinq = [Reflection.Assembly]::Load("System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
  111. $xlinq.GetTypes() | ? { $_.IsPublic -and !$_.IsSerializable -and $_.Name -ne "Extensions" -and !$xlr8r::Get[$_.Name] } | Add-Accelerator
  112.  
  113. Add-Accelerator "Dictionary" "System.Collections.Generic.Dictionary``2, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
  114. Add-Accelerator "PSParser" "System.Management.Automation.PSParser, System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  115. }
  116.  
  117.  
  118. function Get-XmlContent {
  119. #.Synopsis
  120. #   Load an XML file as an XmlDocument
  121. param(
  122.     # Specifies a string that contains the XML to load, or a path to a file which has the XML to load (wildcards are permitted).
  123.     [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  124.     [ValidateNotNullOrEmpty()]
  125.     [Alias("PSPath","Path")]
  126.     [String[]]$Content
  127. ,
  128.     # If set, loads XML with all namespace qualifiers removed, and all entities expanded.
  129.     [Alias("Rn","Rm")]
  130.     [Switch]$RemoveNamespace
  131. )
  132. begin {
  133.     [Text.StringBuilder]$XmlContent = [String]::Empty
  134.     [bool]$Path = $true
  135. }
  136. process {
  137.     if($Path -and ($Path = Test-Path @($Content)[0] -EA 0)) {
  138.         foreach($file in Resolve-Path $Content) {
  139.             $xml = New-Object System.Xml.XmlDocument;
  140.             if($file.Provider.Name -eq "FileSystem") {
  141.                 Write-Verbose $file.ProviderPath
  142.                 $xml.Load( $file.ProviderPath )
  143.             } else {
  144.                 $ofs = "`n"
  145.                 $xml.LoadXml( ([String](Get-Content $file)) )
  146.             }
  147.             if($RemoveNamespace) {
  148.                 [System.Xml.XmlNode[]]$Xml = @(Remove-XmlNamespace -Xml $node)
  149.             }
  150.             Write-Output $xml
  151.         }
  152.     } else {
  153.         # If the "path" parameter isn't actually a path, assume that it's actually content
  154.         foreach($line in $content) {
  155.             $null = $XmlContent.AppendLine( $line )
  156.         }
  157.     }
  158. }
  159. end {
  160.     if(!$Path) {
  161.         $xml = New-Object System.Xml.XmlDocument;
  162.         $xml.LoadXml( $XmlContent.ToString() )
  163.         if($RemoveNamespace) {
  164.             $Xml = @(Remove-XmlNamespace -Xml $xml)
  165.         }
  166.         Write-Output $xml
  167.     }
  168. }}
  169.  
  170.  
  171. Set-Alias Import-Xml Get-XmlContent
  172. Set-Alias ipxml Get-XmlContent
  173. Set-Alias ipx Get-XmlContent
  174. Set-Alias Get-Xml Get-XmlContent
  175. Set-Alias gxml Get-XmlContent
  176. Set-Alias gx Get-XmlContent
  177.  
  178. function Set-XmlContent {
  179. param(
  180.     [Parameter(Mandatory=$true, Position=1)]
  181.     [Alias("PSPath")]
  182.     [String]$Path
  183. ,
  184.     # Specifies one or more XML nodes to search.
  185.     [Parameter(Position=5,ParameterSetName="Xml",Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  186.     [ValidateNotNullOrEmpty()]
  187.     [Alias("Node")]
  188.     [Xml]$Xml
  189. )
  190. process {
  191.     $xml.Save( $Path )
  192. }
  193. }
  194.  
  195. Set-Alias Export-Xml Set-XmlContent
  196. Set-Alias epxml Set-XmlContent
  197. Set-Alias epx Set-XmlContent
  198. Set-Alias Set-Xml Set-XmlContent
  199. Set-Alias sxml Set-XmlContent
  200. Set-Alias sx Set-XmlContent
  201.  
  202. function Format-Xml {
  203. #.Synopsis
  204. #   Pretty-print formatted XML source
  205. #.Description
  206. #   Runs an XmlDocument through an auto-indenting XmlWriter
  207. #.Parameter Xml
  208. #   The Xml Document
  209. #.Parameter Path
  210. #   The path to an xml document (on disc or any other content provider).
  211. #.Parameter Indent
  212. #   The indent level (defaults to 2 spaces)
  213. #.Example
  214. #   [xml]$xml = get-content Data.xml
  215. #   C:\PS>Format-Xml $xml
  216. #.Example
  217. #   get-content Data.xml | Format-Xml
  218. #.Example
  219. #   Format-Xml C:\PS\Data.xml
  220. #.Example
  221. #   ls *.xml | Format-Xml
  222. #
  223. [CmdletBinding()]
  224. param(
  225.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Document")]
  226.    [xml]$Xml
  227. ,
  228.    [Parameter(Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName="File")]
  229.    [Alias("PsPath")]
  230.    [string]$Path
  231. ,
  232.    [Parameter(Mandatory=$false)]
  233.    $Indent=2
  234. )
  235. process {
  236.    ## Load from file, if necessary
  237.    if($Path) { [xml]$xml = Get-Content $Path }
  238.    
  239.    $StringWriter = New-Object System.IO.StringWriter
  240.    $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter
  241.    $xmlWriter.Formatting = "indented"
  242.    $xmlWriter.Indentation = $Indent
  243.    $xml.WriteContentTo($XmlWriter)
  244.    $XmlWriter.Flush()
  245.    $StringWriter.Flush()
  246.    Write-Output $StringWriter.ToString()
  247. }}
  248. Set-
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.