PoshCode Logo PowerShell Code Repository

Xml Module 4.5 by Joel Bennett 20 months ago (modification of post by Joel Bennett view diff)
View followups from Joel Bennett | diff | embed code: <script type="text/javascript" src="http://PoshCode.org/embed/1888"></script>download | new post

My rewritten XML DSL now has better element name handling than last time, and handles null attribute values a little more gracefully. NOTE: if you think that the statement you’re using to pass an attribute value could be null, you should really be very explicit and pass it using the colon syntax like this: -attribute:$value. Also, I fixed a bug when you only have one element.

h4. The other functions round out the set of XML functionality (especially if you don’t have PSCX).

In particular, my Select-XML improves over the built-in Select-XML by leveraging Remove-XmlNamespace to provide a -RemoveNamespace parameter — if it’s supplied, all of the namespace declarations and prefixes are removed from all XML nodes (by an XSL transform) before searching (so you can actually find things, even with namespace-qualified xml). 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, only raw XmlNodes are returned from Select-Xml, so the output isn’t quite 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

  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.  
  29. $xlr8r = [type]::gettype("System.Management.Automation.TypeAccelerators")
  30. $xlinq = [Reflection.Assembly]::Load("System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
  31. $xlinq.GetTypes() | ? { $_.IsPublic -and !$_.IsSerializable -and $_.Name -ne "Extensions" -and !$xlr8r::Get[$_.Name] } | % {
  32.   $xlr8r::Add( $_.Name, $_.FullName )
  33. }
  34. if(!$xlr8r::Get["Stack"]) {
  35.    $xlr8r::Add( "Stack", "System.Collections.Generic.Stack``1, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" )
  36. }
  37. if(!$xlr8r::Get["Dictionary"]) {
  38.    $xlr8r::Add( "Dictionary", "System.Collections.Generic.Dictionary``2, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" )
  39. }
  40. if(!$xlr8r::Get["PSParser"]) {
  41.    $xlr8r::Add( "PSParser", "System.Management.Automation.PSParser, System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" )
  42. }
  43.  
  44.  
  45.  
  46. filter Format-XML {
  47. #.Synopsis
  48. #   Pretty-print formatted XML source
  49. #.Description
  50. #   Runs an XmlDocument through an auto-indenting XmlWriter
  51. #.Parameter Xml
  52. #   The Xml Document
  53. #.Parameter Path
  54. #   The path to an xml document (on disc or any other content provider).
  55. #.Parameter Indent
  56. #   The indent level (defaults to 2 spaces)
  57. #.Example
  58. #   [xml]$xml = get-content Data.xml
  59. #   C:\PS>Format-Xml $xml
  60. #.Example
  61. #   get-content Data.xml | Format-Xml
  62. #.Example
  63. #   Format-Xml C:\PS\Data.xml
  64. #.Example
  65. #   ls *.xml | Format-Xml
  66. #
  67. Param(
  68.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ParameterSetName="Document")]
  69.    [xml]$Xml
  70. ,
  71.    [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ParameterSetName="File")]
  72.    [Alias("PsPath")]
  73.    [string]$Path
  74. ,
  75.    [Parameter(Mandatory=$false)]
  76.    $Indent=2
  77. )
  78.    ## Load from file, if necessary
  79.    if($Path) { [xml]$xml = Get-Content $Path }
  80.    
  81.    $StringWriter = New-Object System.IO.StringWriter
  82.    $XmlWriter = New-Object System.Xml.XmlTextWriter $StringWriter
  83.    $xmlWriter.Formatting = "indented"
  84.    $xmlWriter.Indentation = $Indent
  85.    $xml.WriteContentTo($XmlWriter)
  86.    $XmlWriter.Flush()
  87.    $StringWriter.Flush()
  88.    Write-Output $StringWriter.ToString()
  89. }
  90. Set-Alias fxml Format-Xml -EA 0
  91.  
  92. function Select-Xml {
  93. #.Synopsis
  94. #  The Select-XML cmdlet lets you use XPath queries to search for text in XML strings and documents. Enter an XPath query, and use the Content, Path, or Xml parameter to specify the XML to be searched.
  95. #.Description
  96. #  Improves over the built-in Select-XML by leveraging Remove-XmlNamespace to provide a -RemoveNamespace parameter -- if it's supplied, all of the namespace declarations and prefixes are removed from all XML nodes (by an XSL transform) before searching.  
  97. #  
  98. #  However, only raw XmlNodes are returned from this function, so the output isn't currently compatible with the built in Select-Xml, but is equivalent to using Select-Xml ... | Select-Object -Expand Node
  99. #
  100. #  Also note that if the -RemoveNamespace switch is supplied the returned results *will not* have namespaces in them, even if the input XML did, and entities get expanded automatically.
  101. #.Parameter Content
  102. #  Specifies a string that contains the XML to search. You can also pipe strings to Select-XML.
  103. #.Parameter Namespace
  104. #   Specifies a hash table of the namespaces used in the XML. Use the format @{<namespaceName> = <namespaceUri>}.
  105. #.Parameter Path
  106. #   Specifies the path and file names of the XML files to search.  Wildcards are permitted.
  107. #.Parameter Xml
  108. #  Specifies one or more XML nodes to search.
  109. #.Parameter XPath
  110. #  Specifies an XPath search query. The query language is case-sensitive. This parameter is required.
  111. #.Parameter RemoveNamespace
  112. #  Allows the execution of XPath queries without namespace qualifiers.
  113. #  
  114. #  If you specify the -RemoveNamespace switch, all namespace declarations and prefixes are actually removed from the Xml before the XPath search query is evaluated, and your XPath query should therefore NOT contain any namespace prefixes.
  115. #
  116. #  Note that this means that the returned results *will not* have namespaces in them, even if the input XML did, and entities get expanded automatically.
  117. [CmdletBinding(DefaultParameterSetName="Xml")]
  118. PARAM(
  119.    [Parameter(Position=1,ParameterSetName="Path",Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
  120.    [ValidateNotNullOrEmpty()]
  121.    [Alias("PSPath")]
  122.    [String[]]$Path
  123. ,
  124.    [Parameter(Position=1,ParameterSetName="Xml",Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  125.    [ValidateNotNullOrEmpty()]
  126.    [Alias("Node")]
  127.    [System.Xml.XmlNode[]]$Xml
  128. ,
  129.    [Parameter(ParameterSetName="Content",Mandatory=$true,ValueFromPipeline=$true)]
  130.    [ValidateNotNullOrEmpty()]
  131.    [String[]]$Content
  132. ,
  133.    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  134.    [ValidateNotNullOrEmpty()]
  135.    [Alias("Query")]
  136.    [String[]]$XPath
  137. ,
  138.    [Parameter(Mandatory=$false)]
  139.    [ValidateNotNullOrEmpty()]
  140.    [Hashtable]$Namespace
  141. ,
  142.    [Switch]$RemoveNamespace
  143. )
  144. BEGIN {
  145.    function Select-Node {
  146.    PARAM([Xml.XmlNode]$Xml, [String[]]$XPath, $NamespaceManager)
  147.    BEGIN {
  148.       foreach($node in $xml) {
  149.          if($NamespaceManager -is [Hashtable]) {
  150.             $nsManager = new-object System.Xml.XmlNamespaceManager $node.NameTable
  151.             foreach($ns in $Namespace.GetEnumerator()) {
  152.                $nsManager.AddNamespace( $ns.Key, $ns.Value )
  153.             }
  154.          }
  155.          foreach($path in $xpath) {
  156.             $node.SelectNodes($path, $NamespaceManager)
  157.    }  }  }  }
  158.  
  159.    [Text.StringBuilder]$XmlContent = [String]::Empty
  160. }
  161.  
  162. PROCESS {
  163.    $NSM = $Null; if($PSBoundParameters.ContainsKey("Namespace")) { $NSM = $Namespace }
  164.  
  165.    switch($PSCmdlet.ParameterSetName) {
  166.       "Content" {
  167.          $null = $XmlContent.AppendLine( $Content -Join "`n" )
  168.       }
  169.       "Path" {
  170.          foreach($file in Get-ChildItem $Path) {
  171.             [Xml]$Xml = Get-Content $file
  172.             if($RemoveNamespace) {
  173.                $Xml = Remove-XmlNamespace $Xml
  174.             }
  175.             Select-Node $Xml $XPath  $NSM
  176.          }
  177.       }
  178.       "Xml" {
  179.          foreach($node in $Xml) {
  180.             if($RemoveNamespace) {
  181.                $node = Remove-XmlNamespace $node
  182.             }
  183.             Select-Node $node $XPath $NSM
  184.          }
  185.       }
  186.    }
  187. }
  188. END {
  189.    if($PSCmdlet.ParameterSetName -eq "Content") {
  190.       [Xml]$Xml = $XmlContent.ToString()
  191.       if($RemoveNamespace) {
  192.          $Xml = Remove-XmlNamespace $Xml
  193.       }
  194.       Select-Node $Xml $XPath  $NSM
  195.    }
  196. }
  197.  
  198. }
  199. Set-Alias slxml Select-Xml -EA 0
  200.  
  201. function Convert-Node {
  202. #.Synopsis
  203. # Convert a single XML Node via XSL stylesheets
  204. param(
  205. [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
  206. [System.Xml.XmlReader]$XmlReader,
  207. [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$false)]
  208. [System.Xml.Xsl.XslCompiledTransform]$StyleSheet
  209. )
  210. PROCESS {
  211.    $output = New-Object IO.StringWriter
  212.    $StyleSheet.Transform( $XmlReader, $null, $output )
  213.    Write-Output $output.ToString()
  214. }
  215. }
  216.    
  217. function Convert-Xml {
  218. #.Synopsis
  219. #  The Convert-XML function lets you use Xslt to transform XML strings and documents.
  220. #.Description
  221. #.Parameter Content
  222. #  Specifies a string that contains the XML to search. You can also pipe strings to Select-XML.
  223. #.Parameter Namespace
  224. #   Specifies a hash table of the namespaces used in the XML. Use the format @{<namespaceName> = <namespaceUri>}.
  225. #.Parameter Path
  226. #   Specifies the path and file names of the XML files to search.  Wildcards are permitted.
  227. #.Parameter Xml
  228. #  Specifies one or more XML nodes to search.
  229. #.Parameter Xsl
  230. #  Specifies an Xml StyleSheet to transform with...
  231. [CmdletBinding(DefaultParameterSetName="Xml")]
  232. PARAM(
  233.    [Parameter(Position=1,ParameterSetName="Path",Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
  234.    [ValidateNotNullOrEmpty()]
  235.    [Alias("PSPath")]
  236.    [String[]]$Path
  237. ,
  238.    [Parameter(Position=1,ParameterSetName="Xml",Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  239.    [ValidateNotNullOrEmpty()]
  240.    [Alias("Node")]
  241.    [System.Xml.XmlNode[]]$Xml
  242. ,
  243.    [Parameter(ParameterSetName="Content",Mandatory=$true,ValueFromPipeline=$true)]
  244.    [ValidateNotNullOrEmpty()]
  245.    [String[]]$Content
  246. ,
  247.    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  248.    [ValidateNotNullOrEmpty()]
  249.    [Alias("StyleSheet")]
  250.    [String[]]$Xslt
  251. )
  252. BEGIN {
  253.    $StyleSheet = New-Object System.Xml.Xsl.XslCompiledTransform
  254.    if(Test-Path @($Xslt)[0] -EA 0) {
  255.       Write-Verbose "Loading Stylesheet from $(Resolve-Path @($Xslt)[0])"
  256.       $StyleSheet.Load( (Resolve-Path @($Xslt)[0]) )
  257.    } else {
  258.       Write-Verbose "$Xslt"
  259.       $StyleSheet.Load(([System.Xml.XmlReader]::Create((New-Object System.IO.StringReader ($Xslt -join "`n")))))
  260.    }
  261.    [Text.StringBuilder]$XmlContent = [String]::Empty
  262. }
  263. PROCESS {
  264.    switch($PSCmdlet.ParameterSetName) {
  265.       "Content" {
  266.          $null = $XmlContent.AppendLine( $Content -Join "`n" )
  267.       }
  268.       "Path" {
  269.          foreach($file in Get-ChildItem $Path) {
  270.             Convert-Node -Xml ([System.Xml.XmlReader]::Create((Resolve-Path $file))) $StyleSheet
  271.          }
  272.       }
  273.       "Xml" {
  274.          foreach($node in $Xml) {
  275.             Convert-Node -Xml (New-Object Xml.XmlNodeReader $node) $StyleSheet
  276.          }
  277.       }
  278.    }
  279. }
  280. END {
  281.    if($PSCmdlet.ParameterSetName -eq "Content") {
  282.       [Xml]$Xml = $XmlContent.ToString()
  283.       Convert-Node -Xml $Xml $StyleSheet
  284.    }
  285. }
  286. }
  287. Set-Alias cvxml Convert-Xml -EA 0
  288.  
  289. function Remove-XmlNamespace {
  290. #.Synopsis
  291. #  Removes namespace definitions and prefixes from xml documents
  292. #.Description
  293. #  Runs an xml document through an XSL Transformation to remove namespaces from it if they exist.
  294. #  Entities are also naturally expanded
  295. #.Parameter Content
  296. #  Specifies a string that contains the XML to transform.
  297. #.Parameter Path
  298. #  Specifies the path and file names of the XML files to transform. Wildcards are permitted.
  299. #
  300. #  There will bne one output document for each matching input file.
  301. #.Parameter Xml
  302. #  Specifies one or more XML documents to transform
  303. [CmdletBinding(DefaultParameterSetName="Xml")]
  304. PARAM(
  305.    [Parameter(Position=1,ParameterSetName="Path",Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
  306.    [ValidateNotNullOrEmpty()]
  307.    [Alias("PSPath")]
  308.    [String[]]$Path
  309. ,
  310.    [Parameter(Position=1,ParameterSetName="Xml",Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  311.    [ValidateNotNullOrEmpty()]
  312.    [Alias("Node")]
  313.    [System.Xml.XmlNode[]]$Xml
  314. ,
  315.    [Parameter(ParameterSetName="Content",Mandatory=$true,ValueFromPipeline=$true)]
  316.    [ValidateNotNullOrEmpty()]
  317.    [String[]]$Content
  318. ,
  319.    [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$false)]
  320.    [ValidateNotNullOrEmpty()]
  321.    [Alias("StyleSheet")]
  322.    [String[]]$Xslt
  323. )
  324. BEGIN {
  325.    $StyleSheet = New-Object System.Xml.Xsl.XslCompiledTransform
  326.    $StyleSheet.Load(([System.Xml.XmlReader]::Create((New-Object System.IO.StringReader @"
  327. <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  328.   <xsl:output method="xml" indent="yes"/>
  329.   <xsl:template match="/|comment()|processing-instruction()">
  330.      <xsl:copy>
  331.         <xsl:apply-templates/>
  332.      </xsl:copy>
  333.   </xsl:template>
  334.  
  335.   <xsl:template match="*">
  336.      <xsl:element name="{local-name()}">
  337.         <xsl:apply-templates select="@*|node()"/>
  338.      </xsl:element>
  339.   </xsl:template>
  340.  
  341.   <xsl:template match="@*">
  342.      <xsl:attribute name="{local-name()}">
  343.         <xsl:value-of select="."/>
  344.      </xsl:attribute>
  345.   </xsl:template>
  346. </xsl:stylesheet>
  347. "@))))
  348.    [Text.StringBuilder]$XmlContent = [String]::Empty
  349. }
  350. PROCESS {
  351.    switch($PSCmdlet.ParameterSetName) {
  352.       "Content" {
  353.          $null = $XmlContent.AppendLine( $Content -Join "`n" )
  354.       }
  355.       "Path" {
  356.          foreach($file in Get-ChildItem $Path) {
  357.             [Xml]$Xml = Get-Content $file
  358.             Convert-Node -Xml $Xml $StyleSheet
  359.          }
  360.       }
  361.       "Xml" {
  362.          $Xml | Convert-Node $StyleSheet
  363.       }
  364.    }
  365. }
  366. END {
  367.    if($PSCmdlet.ParameterSetName -eq "Content") {
  368.       [Xml]$Xml = $XmlContent.ToString()
  369.       Convert-Node -Xml $Xml $StyleSheet
  370.    }
  371. }
  372. }
  373. Set-Alias rmns Remove-XmlNamespace -EA 0
  374.  
  375.  
  376.  
  377. function New-XDocument {
  378. #.Synopsis
  379. #   Creates a new XDocument (the new xml document type)
  380. #.Description
  381. #  This is the root for a new XML mini-dsl, akin to New-BootsWindow for XAML
  382. #  It creates a new XDocument, and takes scritpblock(s) to define it's contents
  383. #.Parameter root
  384. #   The root node name
  385. #.Parameter version
  386. #   Optional: the XML version. Defaults to 1.0
  387. #.Parameter encoding
  388. #   Optional: the Encoding. Defaults to UTF-8
  389. #.Parameter standalone
  390. #  Optional: whether to specify standalone in the xml declaration. Defaults to "yes"
  391. #.Parameter args
  392. #   this is where all the dsl magic happens. Please see the Examples. :)
  393. #
  394. #.Example
  395. # [string]$xml = New-XDocument rss -version "2.0" {
  396. #    channel {
  397. #       title {"Test RSS Feed"}
  398. #       link {"http`://HuddledMasses.org"}
  399. #       description {"An RSS Feed generated simply to demonstrate my XML DSL"}
  400. #       item {
  401. #          title {"The First Item"}
  402. #          link {"http`://huddledmasses.org/new-site-new-layout-lost-posts/"}
  403. #          guid -isPermaLink true {"http`://huddledmasses.org/new-site-new-layout-lost-posts/"}
  404. #          description {"Ema Lazarus' Poem"}
  405. #          pubDate {(Get-Date 10/31/2003 -f u) -replace " ","T"}
  406. #       }
  407. #    }
  408. # }
  409. #
  410. # C:\PS>$xml.Declaration.ToString()  ## I can't find a way to have this included in the $xml.ToString()
  411. # C:\PS>$xml.ToString()
  412. #
  413. # <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  414. # <rss version="2.0">
  415. #   <channel>
  416. #     <title>Test RSS Feed</title>
  417. #     <link>http ://HuddledMasses.org</link>
  418. #     <description>An RSS Feed generated simply to demonstrate my XML DSL</description>
  419. #     <item>
  420. #       <title>The First Item</title>
  421. #       <link>http ://huddledmasses.org/new-site-new-layout-lost-posts/</link>
  422. #       <guid isPermaLink="true">http ://huddledmasses.org/new-site-new-layout-lost-posts/</guid>
  423. #       <description>Ema Lazarus' Poem</description>
  424. #       <pubDate>2003-10-31T00:00:00Z</pubDate>
  425. #     </item>
  426. #   </channel>
  427. # </rss>
  428. #
  429. #
  430. # Description
  431. # -----------
  432. # This example shows the creation of a complete RSS feed with a single item in it.
  433. #
  434. # NOTE that the backtick in the http`: in the URLs in the input is unecessary, and I added the space after the http: in the URLs  in the output -- these are accomodations to PoshCode's spam filter. Backticks are not need in the input, and spaces do not appear in the actual output.
  435. #
  436. #
  437. #.Example
  438. # [XNamespace]$atom="http`://www.w3.org/2005/Atom"
  439. # C:\PS>[XNamespace]$dc = "http`://purl.org/dc/elements/1.1"
  440. #
  441. # C:\PS>New-XDocument ($atom + "feed") -Encoding "UTF-16" -$([XNamespace]::Xml +'lang') "en-US" -dc $dc {
  442. #    title {"Test First Entry"}
  443. #    link {"http`://HuddledMasses.org"}
  444. #    updated {(Get-Date -f u) -replace " ","T"}
  445. #    author {
  446. #       name {"Joel Bennett"}
  447. #       uri {"http`://HuddledMasses.org"}
  448. #    }
  449. #    id {"http`://huddledmasses.org/" }
  450. #
  451. #    entry {
  452. #       title {"Test First Entry"}
  453. #       link {"http`://HuddledMasses.org/new-site-new-layout-lost-posts/" }
  454. #       id {"http`://huddledmasses.org/new-site-new-layout-lost-posts/" }
  455. #       updated {(Get-Date 10/31/2003 -f u) -replace " ","T"}
  456. #       summary {"Ema Lazarus' Poem"}
  457. #       link -rel license -href "http`://creativecommons.org/licenses/by/3.0/" -title "CC By-Attribution"
  458. #       dc:rights { "Copyright 2009, Some rights reserved (licensed under the Creative Commons Attribution 3.0 Unported license)" }
  459. #       category -scheme "http`://huddledmasses.org/tag/" -term "huddled-masses"
  460. #    }
  461. # } | % { $_.Declaration.ToString(); $_.ToString() }
  462. #
  463. # <?xml version="1.0" encoding="UTF-16" standalone="yes"?>
  464. # <feed xml:lang="en-US" xmlns="http ://www.w3.org/2005/Atom">
  465. #   <title>Test First Entry</title>
  466. #   <link>http ://HuddledMasses.org</link>
  467. #   <updated>2009-07-29T17:25:49Z</updated>
  468. #   <author>
  469. #      <name>Joel Bennett</name>
  470. #      <uri>http ://HuddledMasses.org</uri>
  471. #   </author>
  472. #   <id>http ://huddledmasses.org/</id>
  473. #   <entry>
  474. #     <title>Test First Entry</title>
  475. #     <link>http ://HuddledMasses.org/new-site-new-layout-lost-posts/</link>
  476. #     <id>http ://huddledmasses.org/new-site-new-layout-lost-posts/</id>
  477. #     <updated>2003-10-31T00:00:00Z</updated>
  478. #     <summary>Ema Lazarus' Poem</summary>
  479. #     <link rel="license" href="http ://creativecommons.org/licenses/by/3.0/" title="CC By-Attribution" />
  480. #     <dc:rights>Copyright 2009, Some rights reserved (licensed under the Creative Commons Attribution 3.0 Unported license)</dc:rights>
  481. #     <category scheme="http ://huddledmasses.org/tag/" term="huddled-masses" />
  482. #   </entry>
  483. # </feed>
  484. #
  485. #
  486. # Description
  487. # -----------
  488. # This example shows the use of a default namespace, as well as additional specific namespaces for the "dc" namespace. It also demonstrates how you can get the <?xml?> declaration which does not appear in a simple .ToString().
  489. #
  490. # NOTE that the backtick in the http`: in the URLs in the input is unecessary, and I added the space after the http: in the URLs  in the output -- these are accomodations to PoshCode's spam filter. Backticks are not need in the input, and spaces do not appear in the actual output.#
  491. #
  492. Param(
  493.    [Parameter(Mandatory = $true, Position = 0)]
  494.    [System.Xml.Linq.XName]$root
  495. ,
  496.    [Parameter(Mandatory = $false)]
  497.    [string]$Version = "1.0"
  498. ,
  499.    [Parameter(Mandatory = $false)]
  500.    [string]$Encoding = "UTF-8"
  501. ,
  502.    [Parameter(Mandatory = $false)]
  503.    [string]$Standalone = "yes"
  504. ,
  505.    [AllowNull()][AllowEmptyString()][AllowEmptyCollection()]
  506.    [Parameter(Position=99, Mandatory = $false, ValueFromRemainingArguments=$true)]
  507.    [PSObject[]]$args
  508. )
  509. BEGIN {
  510.    $script:NameSpaceHash = New-Object 'Dictionary[String,XNamespace]'
  511.    if($root.NamespaceName) {
  512.       $script:NameSpaceHash.Add("", $root.Namespace)
  513.    }
  514. }
  515. PROCESS {
  516.    New-Object XDocument (New-Object XDeclaration $Version, $Encoding, $standalone),(
  517.       New-Object XElement $(
  518.          $root
  519.          while($args) {
  520.             $attrib, $value, $args = $args
  521.             if($attrib -is [ScriptBlock]) {
  522.                # Write-Verbose "Preparsed DSL: $attrib"
  523.                $attrib = ConvertFrom-XmlDsl $attrib
  524.                Write-Verbose "Reparsed DSL: $attrib"
  525.                &$attrib
  526.             } elseif ( $value -is [ScriptBlock] -and "-CONTENT".StartsWith($attrib.TrimEnd(':').ToUpper())) {
  527.                $value = ConvertFrom-XmlDsl $value
  528.                &$value
  529.             } elseif ( $value -is [XNamespace]) {
  530.                New-Object XAttribute ([XNamespace]::Xmlns + $attrib.TrimStart("-").TrimEnd(':')), $value
  531.                $script:NameSpaceHash.Add($attrib.TrimStart("-").TrimEnd(':'), $value)
  532.             } else {
  533.                New-Object XAttribute $attrib.TrimStart("-").TrimEnd(':'), $value
  534.             }
  535.          }
  536.       ))
  537. }
  538. }
  539.  
  540. Set-Alias xml New-XDocument -EA 0
  541. Set-Alias New-Xml New-XDocument -EA 0
  542.  
  543. function New-XAttribute {
  544. #.Synopsys
  545. #   Creates a new XAttribute (an xml attribute on an XElement for XDocument)
  546. #.Description
  547. #  This is the work-horse for the XML mini-dsl
  548. #.Parameter name
  549. #   The attribute name
  550. #.Parameter value
  551. #  The attribute value
  552. Param([Parameter(Mandatory=$true)]$name,[Parameter(Mandatory=$true)]$value)
  553.    New-Object XAttribute $name, $value
  554. }
  555. Set-Alias xa New-XAttribute -EA 0
  556. Set-Alias New-XmlAttribute New-XAttribute -EA 0
  557.  
  558.  
  559. function New-XElement {
  560. #.Synopsys
  561. #   Creates a new XElement (an xml tag for XDocument)
  562. #.Description
  563. #  This is the work-horse for the XML mini-dsl
  564. #.Parameter tag
  565. #   The name of the xml tag
  566. #.Parameter args
  567. #   this is where all the dsl magic happens. Please see the Examples. :)
  568. Param(
  569.    [Parameter(Mandatory = $true, Position = 0)]
  570.    [System.Xml.Linq.XName]$tag
  571. ,
  572.    [AllowNull()][AllowEmptyString()][AllowEmptyCollection()]
  573.    [Parameter(Position=99, Mandatory = $false, ValueFromRemainingArguments=$true)]
  574.    [PSObject[]]$args
  575. )
  576. #  BEGIN {
  577.    #  if([string]::IsNullOrEmpty( $tag.NamespaceName )) {
  578.       #  $tag = $($script:NameSpaceStack.Peek()) + $tag
  579.       #  if( $script:NameSpaceStack.Count -gt 0 ) {
  580.          #  $script:NameSpaceStack.Push( $script:NameSpaceStack.Peek() )
  581.       #  } else {
  582.          #  $script:NameSpaceStack.Push( $null )
  583.       #  }      
  584.    #  } else {
  585.       #  $script:NameSpaceStack.Push( $tag.Namespace )
  586.    #  }
  587. #  }
  588. PROCESS {
  589.   New-Object XElement $(
  590.      $tag
  591.      while($args) {
  592.         $attrib, $value, $args = $args
  593.         if($attrib -is [ScriptBlock]) { # then it's content
  594.            &$attrib
  595.         } elseif ( $value -is [ScriptBlock] -and "-CONTENT".StartsWith($attrib.TrimEnd(':').ToUpper())) { # then it's content
  596.            &$value
  597.         } elseif ( $value -is [XNamespace]) {
  598.            New-Object XAttribute ([XNamespace]::Xmlns + $attrib.TrimStart("-").TrimEnd(':')), $value
  599.            $script:NameSpaceHash.Add($attrib.TrimStart("-").TrimEnd(':'), $value)
  600.         } elseif($value -match "-(?!\d)\w") {
  601.             $args = @($value)+@($args)
  602.         } elseif($value -ne $null) {
  603.            New-Object XAttribute $attrib.TrimStart("-").TrimEnd(':'), $value
  604.         }        
  605.        
  606.      }
  607.    )
  608. }
  609. #  END {
  610.    #  $null = $script:NameSpaceStack.Pop()
  611. #  }
  612. }
  613. Set-Alias xe New-XElement
  614. Set-Alias New-XmlElement New-XElement
  615.  
  616. function ConvertFrom-XmlDsl {
  617. Param([ScriptBlock]$script)
  618.    $parserrors = $null
  619.    $global:tokens = [PSParser]::Tokenize( $script, [ref]$parserrors )
  620.    [Array]$duds = $global:tokens | Where-Object { $_.Type -eq "Command" -and !$_.Content.Contains('-') -and ($(Get-Command $_.Content -Type Cmdlet,Function,ExternalScript -EA 0) -eq $Null) }
  621.    [Array]::Reverse( $duds )
  622.    
  623.    [string[]]$ScriptText = "$script" -split "`n"
  624.  
  625.    ForEach($token in $duds ) {
  626.       # replace : notation with namespace notation
  627.       if( $token.Content.Contains(":") ) {
  628.          $key, $localname = $token.Content -split ":"
  629.          $ScriptText[($token.StartLine - 1)] = $ScriptText[($token.StartLine - 1)].Remove( $token.StartColumn -1, $token.Length ).Insert( $token.StartColumn -1, "'" + $($script:NameSpaceHash[$key] + $localname) + "'" )
  630.       } else {
  631.          $ScriptText[($token.StartLine - 1)] = $ScriptText[($token.StartLine - 1)].Remove( $token.StartColumn -1, $token.Length ).Insert( $token.StartColumn -1, "'" + $($script:NameSpaceHash[''] + $token.Content) + "'" )
  632.       }
  633.       # insert 'xe' before everything (unless it's a valid command)
  634.       $ScriptText[($token.StartLine - 1)] = $ScriptText[($token.StartLine - 1)].Insert( $token.StartColumn -1, "xe " )
  635.    }
  636.    Write-Output ([ScriptBlock]::Create( ($ScriptText -join "`n") ))
  637. }
  638.    
  639. Export-ModuleMember -alias * -function New-XDocument, New-XAttribute, New-XElement, Remove-XmlNamespace, Convert-Xml, Select-Xml, Format-Xml
  640.  
  641. # SIG # Begin signature block
  642. # MIIRDAYJKoZIhvcNAQcCoIIQ/TCCEPkCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
  643. # gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
  644. # AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUmrZlx69zCgoayALV1qJAbLV6
  645. # fAiggg5CMIIHBjCCBO6gAwIBAgIBFTANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQG
  646. # EwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERp
  647. # Z2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2Vy
  648. # dGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDcxMDI0MjIwMTQ1WhcNMTIxMDI0MjIw
  649. # MTQ1WjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzAp
  650. # BgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRlIFNpZ25pbmcxODA2BgNV
  651. # BAMTL1N0YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJbnRlcm1lZGlhdGUgT2JqZWN0
  652. # IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyiOLIjUemqAbPJ1J
  653. # 0D8MlzgWKbr4fYlbRVjvhHDtfhFN6RQxq0PjTQxRgWzwFQNKJCdU5ftKoM5N4YSj
  654. # Id6ZNavcSa6/McVnhDAQm+8H3HWoD030NVOxbjgD/Ih3HaV3/z9159nnvyxQEckR
  655. # ZfpJB2Kfk6aHqW3JnSvRe+XVZSufDVCe/vtxGSEwKCaNrsLc9pboUoYIC3oyzWoU
  656. # TZ65+c0H4paR8c8eK/mC914mBo6N0dQ512/bkSdaeY9YaQpGtW/h/W/FkbQRT3sC
  657. # pttLVlIjnkuY4r9+zvqhToPjxcfDYEf+XD8VGkAqle8Aa8hQ+M1qGdQjAye8OzbV
  658. # uUOw7wIDAQABo4ICfzCCAnswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYD
  659. # VR0OBBYEFNBOD0CZbLhLGW87KLjg44gHNKq3MIGoBgNVHSMEgaAwgZ2AFE4L7xqk
  660. # QFulF2mHMMo0aEPQQa7yoYGBpH8wfTELMAkGA1UEBhMCSUwxFjAUBgNVBAoTDVN0
  661. # YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmljYXRl
  662. # IFNpZ25pbmcxKTAnBgNVBAMTIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9y
  663. # aXR5ggEBMAkGA1UdEgQCMAAwPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzAChiFo
  664. # dHRwOi8vd3d3LnN0YXJ0c3NsLmNvbS9zZnNjYS5jcnQwYAYDVR0fBFkwVzAsoCqg
  665. # KIYmaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3Nmc2NhLWNybC5jcmwwJ6AloCOG
  666. # IWh0dHA6Ly9jcmwuc3RhcnRzc2wuY29tL3Nmc2NhLmNybDCBggYDVR0gBHsweTB3
  667. # BgsrBgEEAYG1NwEBBTBoMC8GCCsGAQUFBwIBFiNodHRwOi8vY2VydC5zdGFydGNv
  668. # bS5vcmcvcG9saWN5LnBkZjA1BggrBgEFBQcCARYpaHR0cDovL2NlcnQuc3RhcnRj
  669. # b20ub3JnL2ludGVybWVkaWF0ZS5wZGYwEQYJYIZIAYb4QgEBBAQDAgABMFAGCWCG
  670. # SAGG+EIBDQRDFkFTdGFydENvbSBDbGFzcyAyIFByaW1hcnkgSW50ZXJtZWRpYXRl
  671. # IE9iamVjdCBTaWduaW5nIENlcnRpZmljYXRlczANBgkqhkiG9w0BAQUFAAOCAgEA
  672. # UKLQmPRwQHAAtm7slo01fXugNxp/gTJY3+aIhhs8Gog+IwIsT75Q1kLsnnfUQfbF
  673. # pl/UrlB02FQSOZ+4Dn2S9l7ewXQhIXwtuwKiQg3NdD9tuA8Ohu3eY1cPl7eOaY4Q
  674. # qvqSj8+Ol7f0Zp6qTGiRZxCv/aNPIbp0v3rD9GdhGtPvKLRS0CqKgsH2nweovk4h
  675. # fXjRQjp5N5PnfBW1X2DCSTqmjweWhlleQ2KDg93W61Tw6M6yGJAGG3GnzbwadF9B
  676. # UW88WcRsnOWHIu1473bNKBnf1OKxxAQ1/3WwJGZWJ5UxhCpA+wr+l+NbHP5x5XZ5
  677. # 8xhhxu7WQ7rwIDj8d/lGU9A6EaeXv3NwwcbIo/aou5v9y94+leAYqr8bbBNAFTX1
  678. # pTxQJylfsKrkB8EOIx+Zrlwa0WE32AgxaKhWAGho/Ph7d6UXUSn5bw2+usvhdkW4
  679. # npUoxAk3RhT3+nupi1fic4NG7iQG84PZ2bbS5YxOmaIIsIAxclf25FwssWjieMwV
  680. # 0k91nlzUFB1HQMuE6TurAakS7tnIKTJ+ZWJBDduUbcD1094X38OvMO/++H5S45Ki
  681. # 3r/13YTm0AWGOvMFkEAF8LbuEyecKTaJMTiNRfBGMgnqGBfqiOnzxxRVNOw2hSQp
  682. # 0B+C9Ij/q375z3iAIYCbKUd/5SSELcmlLl+BuNknXE0wggc0MIIGHKADAgECAgFR
  683. # MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRD
  684. # b20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2ln
  685. # bmluZzE4MDYGA1UEAxMvU3RhcnRDb20gQ2xhc3MgMiBQcmltYXJ5IEludGVybWVk
  686. # aWF0ZSBPYmplY3QgQ0EwHhcNMDkxMTExMDAwMDAxWhcNMTExMTExMDYyODQzWjCB
  687. # qDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMRcwFQYDVQQHEw5XZXN0
  688. # IEhlbnJpZXR0YTEtMCsGA1UECxMkU3RhcnRDb20gVmVyaWZpZWQgQ2VydGlmaWNh
  689. # dGUgTWVtYmVyMRUwEwYDVQQDEwxKb2VsIEJlbm5ldHQxJzAlBgkqhkiG9w0BCQEW
  690. # GEpheWt1bEBIdWRkbGVkTWFzc2VzLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEP
  691. # ADCCAQoCggEBAMfjItJjMWVaQTECvnV/swHQP0FTYUvRizKzUubGNDNaj7v2dAWC
  692. # rAA+XE0lt9JBNFtCCcweDzphbWU/AAY0sEPuKobV5UGOLJvW/DcHAWdNB/wRrrUD
  693. # dpcsapQ0IxxKqpRTrbu5UGt442+6hJReGTnHzQbX8FoGMjt7sLrHc3a4wTH3nMc0
  694. # U/TznE13azfdtPOfrGzhyBFJw2H1g5Ag2cmWkwsQrOBU+kFbD4UjxIyus/Z9UQT2
  695. # R7bI2R4L/vWM3UiNj4M8LIuN6UaIrh5SA8q/UvDumvMzjkxGHNpPZsAPaOS+RNmU
  696. # Go6X83jijjbL39PJtMX+doCjS/lnclws5lUCAwEAAaOCA4EwggN9MAkGA1UdEwQC
  697. # MAAwDgYDVR0PAQH/BAQDAgeAMDoGA1UdJQEB/wQwMC4GCCsGAQUFBwMDBgorBgEE
  698. # AYI3AgEVBgorBgEEAYI3AgEWBgorBgEEAYI3CgMNMB0GA1UdDgQWBBR5tWPGCLNQ
  699. # yCXI5fY5ViayKj6xATCBqAYDVR0jBIGgMIGdgBTQTg9AmWy4SxlvOyi44OOIBzSq
  700. # t6GBgaR/MH0xCzAJBgNVBAYTAklMMRYwFAYDVQQKEw1TdGFydENvbSBMdGQuMSsw
  701. # KQYDVQQLEyJTZWN1cmUgRGlnaXRhbCBDZXJ0aWZpY2F0ZSBTaWduaW5nMSkwJwYD
  702. # VQQDEyBTdGFydENvbSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eYIBFTCCAUIGA1Ud
  703. # IASCATkwggE1MIIBMQYLKwYBBAGBtTcBAgEwggEgMC4GCCsGAQUFBwIBFiJodHRw
  704. # Oi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMDQGCCsGAQUFBwIBFihodHRw
  705. # Oi8vd3d3LnN0YXJ0c3NsLmNvbS9pbnRlcm1lZGlhdGUucGRmMIG3BggrBgEFBQcC
  706. # AjCBqjAUFg1TdGFydENvbSBMdGQuMAMCAQEagZFMaW1pdGVkIExpYWJpbGl0eSwg
  707. # c2VlIHNlY3Rpb24gKkxlZ2FsIExpbWl0YXRpb25zKiBvZiB0aGUgU3RhcnRDb20g
  708. # Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUG9saWN5IGF2YWlsYWJsZSBhdCBodHRw
  709. # Oi8vd3d3LnN0YXJ0c3NsLmNvbS9wb2xpY3kucGRmMGMGA1UdHwRcMFowK6ApoCeG
  710. # JWh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2NydGMyLWNybC5jcmwwK6ApoCeGJWh0
  711. # dHA6Ly9jcmwuc3RhcnRzc2wuY29tL2NydGMyLWNybC5jcmwwgYkGCCsGAQUFBwEB
  712. # BH0wezA3BggrBgEFBQcwAYYraHR0cDovL29jc3Auc3RhcnRzc2wuY29tL3N1Yi9j
  713. # bGFzczIvY29kZS9jYTBABggrBgEFBQcwAoY0aHR0cDovL3d3dy5zdGFydHNzbC5j
  714. # b20vY2VydHMvc3ViLmNsYXNzMi5jb2RlLmNhLmNydDAjBgNVHRIEHDAahhhodHRw
  715. # Oi8vd3d3LnN0YXJ0c3NsLmNvbS8wDQYJKoZIhvcNAQEFBQADggEBACY+J88ZYr5A
  716. # 6lYz/L4OGILS7b6VQQYn2w9Wl0OEQEwlTq3bMYinNoExqCxXhFCHOi58X6r8wdHb
  717. # E6mU8h40vNYBI9KpvLjAn6Dy1nQEwfvAfYAL8WMwyZykPYIS/y2Dq3SB2XvzFy27
  718. # zpIdla8qIShuNlX22FQL6/FKBriy96jcdGEYF9rbsuWku04NqSLjNM47wCAzLs/n
  719. # FXpdcBL1R6QEK4MRhcEL9Ho4hGbVvmJES64IY+P3xlV2vlEJkk3etB/FpNDOQf8j
  720. # RTXrrBUYFvOCv20uHsRpc3kFduXt3HRV2QnAlRpG26YpZN4xvgqSGXUeqRceef7D
  721. # dm4iTdHK5tIxggI0MIICMAIBATCBkjCBjDELMAkGA1UEBhMCSUwxFjAUBgNVBAoT
  722. # DVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBEaWdpdGFsIENlcnRpZmlj
  723. # YXRlIFNpZ25pbmcxODA2BgNVBAMTL1N0YXJ0Q29tIENsYXNzIDIgUHJpbWFyeSBJ
  724. # bnRlcm1lZGlhdGUgT2JqZWN0IENBAgFRMAkGBSsOAwIaBQCgeDAYBgorBgEEAYI3
  725. # AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisG
  726. # AQQBgjcCAQsxDjAMBgorBgEEAYI3AgEWMCMGCSqGSIb3DQEJBDEWBBTjyXflYZKj
  727. # by4lmx0Jd4HX0SJm5zANBgkqhkiG9w0BAQEFAASCAQAREDiyhjKl4JSCX61rAtoU
  728. # oJ7Ly1O1dKFdR5+bt0ghet9uK9qzqAl9+4fxb4RC/XqncXYBumtaSwHcjhyn5G68
  729. # HycOhdY3g+4euz6xo/bPfBFVSz3cA0A0oPu5S2cat+jftnWMbk3Y+jZsc7g9SbIZ
  730. # 0MGAf808lqkZuTdwG/dH8JdSCk3Yw11pjss8zz2AXHg5FFH24ZKR3zLpnSSHX0Ln
  731. # XMVRrktKWHF/BWl4PDOK06u4ZZgw3v1X0vifDMVy89XRwN1Oc6j22AuX/ob9Zkih
  732. # DzapWeedZIOscR0tyFR6DOrD6gj8ytsp/Iy1T8taP99GZPHiEnjsc479LXeyarVt
  733. # SIG # End signature block

Submit a correction or amendment below (
click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:


Remember me