Referencing:
https://www.red-gate.com/simple-talk/sysadmin/powershell/powershell-data-basics-xml/
and:
https://stackoverflow.com/a/65264118/4531180
how is a list of elements and their properties printed?
PS /home/nicholas/powershell> PS /home/nicholas/powershell> $doc = new-object System.Xml.XmlDocument PS /home/nicholas/powershell> $file = resolve-path('./bookstore.xml') PS /home/nicholas/powershell> $doc.load($file) PS /home/nicholas/powershell> PS /home/nicholas/powershell> $doc.bookstore.book[1].author.first-name ParserError: Line | 1 | $doc.bookstore.book[1].author.first-name | ~~~~~ | Unexpected token '-name' in expression or statement. PS /home/nicholas/powershell> PS /home/nicholas/powershell> $doc.bookstore.book[1].author first-name last-name ---------- --------- Margaret Atwood PS /home/nicholas/powershell> PS /home/nicholas/powershell> $doc.bookstore bk book -- ---- urn:samples {book, book, book, book} PS /home/nicholas/powershell>
Advertisement
Answer
To address the for-display formatting problem:
If you look closely at the sample output from your own answer, you’ll see that even though the display is mostly helpful, the value of the author
property is author
instead of showing the (presumed) first-name
and last-name
child-element values.
The problem is that PowerShell’s default output formatting represents child elements that have:
- at least one attribute
- and/or at least one child element themselves
by the element’s name only.
Especially with deeply nested elements this results in unhelpful output.
Workarounds, possibly in combination:
Access the
.OuterXml
or.InnerXml
property of such elements, which contains the full XML text of the element with / without the element’s tags themselves.This will likely only be helpful with perhaps at most another level of nesting will be visually helpful, given that the XML text is a single-line representation that is not pretty-printed.
You can pipe
.OuterXml
/InnerXml
values to a pretty-printing function, which requires some extra work, however, because no such functionality is directly exposed by PowerShell.
Use
Select-Object
(or, for display purposes only, aFormat-*
cmdlet such asFormat-Table
) with calculated properties.- While this allows you full control over what is displayed, it is more work.
See the examples below.
# Sample XML document $xmlDoc = [xml] @" <?xml version="1.0"?> <bookstore> <book id="bk101"> <author> <first-name>Matthew</first-name> <last-name>Gambardella</last-name> </author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> <book id="bk102"> <author> <first-name>Kim</first-name> <last-name>Rall</last-name> </author> <title>Midnight Rain</title> <genre>Fantasy</genre> <price>5.95</price> <publish_date>2000-12-16</publish_date> <description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description> </book> </bookstore> "@
To get a helpful representation of all <book>
elements including the <author>
child elements’ <first-name>
and <last-name
> child elements via Select-Object
and a calculated property:
$xmldoc.bookstore.book | Select-Object id, @{ n='author'; e={ $_.author.'first-name' + ' ' + $_.author.'last-name'} }, title, genre, price, publish_date, description
This yields (note how the author
property now lists first and last name):
id : bk101 author : Matthew Gambardella title : XML Developer's Guide genre : Computer price : 44.95 publish_date : 2000-10-01 description : An in-depth look at creating applications with XML. id : bk102 author : Kim Rall title : Midnight Rain genre : Fantasy price : 5.95 publish_date : 2000-12-16 description : A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.
To get a helpful representation of all <book>
elements via pretty-printed XML, via an auxiliary System.Xml.Linq.XDocument
instance:
# Load the assembly that contains XDocument. # Note: Required in Windows PowerShell only, and only once per session. Add-Type -AssemblyName System.Xml.Linq $xmldoc.bookstore.book | ForEach-Object { ([System.Xml.Linq.XDocument] $_.OuterXml).ToString() }
This yields (a pretty-printed XML representation):
<book id="bk101"> <author> <first-name>Matthew</first-name> <last-name>Gambardella</last-name> </author> <title>XML Developer's Guide</title> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> <book id="bk102"> <author> <first-name>Kim</first-name> <last-name>Rall</last-name> </author> <title>Midnight Rain</title> <genre>Fantasy</genre> <price>5.95</price> <publish_date>2000-12-16</publish_date> <description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description> </book>
Note that you could wrap the formatting code in a simple (filter) function named Format-Xml
that you could put in your $PROFILE
file (in Windows PowerShell, also place the Add-Type -AssemblyName System.Xml.Linq
there, above it):
filter Format-Xml { ([System.Xml.Linq.XDocument] $_.OuterXml).ToString() }
Now the formatting is as simple as:
$xmldoc.bookstore.book | Format-Xml