How to Add Data to an XML File Using Windows PowerShell

How to Add Data to an XML File Using Windows PowerShell

  • Comments 6
  • Likes

Hey, Scripting Guy! QuestionHey, Scripting Guy! I need to be able to use Windows PowerShell to add data to an XML file. Unfortunately, there is no Add-XML cmdlet available. I thought about using the Windows PowerShell Add-Content cmdlet and trying to write my data to the file, but it never worked correctly. I never could get all of those angle thingies to line up properly. There has got to be a better way to do this, but after spending several hours Binging around on the Internet I finally gave up, and decided to write you.

-- JS

 

Hey, Scripting Guy! AnswerHello JS,

Microsoft Scripting Guy Ed Wilson here. It is amazing how quickly things change. It reminds me of the weather when I was in Lisbon, Portugal. The saying was if you did not like the weather, just wait a few minutes and it will change. The sunsets were stunning. I enjoyed walking along the waterfront and took the following photo on one of those walks.

Photo Ed took of sunset in Lisbon

 

JS, I was thinking about creating a here-string that would contain the elements for an XML file, substitute values for the variables contained in the here-string, and then write that to a file. However, as I thought about it, I decided it might be error prone. I then thought about using the methods from the system.xml.xmldocument .NET Framework class to add elements and childnodes to an existing XML document. However, that ended up being too complicated. So I decided to cheat. The complete Add-XmlContentToFile.ps1 Windows PowerShell script is shown here.

Add-XmlContentToFile.ps1

$path = "C:\fso\books.xml"
$csvPath = "C:\fso\books.csv"
$doc = [xml](Get-Content -Path $path)
foreach($e in (Import-Csv -Path $csvPath))
{
 
$element = $Doc.catalog.book[0].clone()
 
$element.id = $e.id
 
$element.author = $e.author
 
$element.title = $e.title
 
$element.genre = $e.genre
 
$element.price = $e.price
 
$element.publish_date = $e.publish_date
 
$element.description = $e.description
 
$doc.DocumentElement.AppendChild($element)
}
$doc.Save("C:\fso\x.xml")

To illustrate adding content to an XML file, I am going to use the books.xml file that I copied from MSDN. The books.xml file contains a number of books and stores some important information about each book, such as author, title, price, and genre. This is shown in XML Notepad in the following image.

Image of books.xml file in XML Notepad

 

The source code for the books.xml file is not as pretty as that shown in XML Notepad. You can open the file in Internet Explorer, but it is little better than Notepad, as shown in the following image.

Image of books.xml file in Notepad

 

The data that must be added to the books.xml file is contained in a CSV file named books.csv. The contents of books.csv are shown in the following image.

Image of contents of books.csv file

 

The first thing the Add-XMLContentToFile.ps1 script does, after creating a couple of variables to hold the path to the XML file and to the CSV file, is use the Get-Content cmdlet to read the XML file, and to use the [xml] type accelerator to convert the contents into an XML document. This is shown here:

$doc = [xml](Get-Content -Path $path)
 

Piping the $doc variable to the Get-Member cmdlet confirms that you are now working with an instance of the System.XML.XMLDocument .NET Framework class.

You can access the different elements of the books.xml file by using the same dotted notation you would use to work with any other object. The first node is catalog. This is shown here:

PS C:\> $doc = [xml](Get-content c:\fso\books.xml)
PS C:\> $doc

xml                                        catalog
---                                        -------
version="1.0"                              catalog


PS C:\> $doc.catalog

book
----
{book, book, book, book...}


PS C:\>
 

Because the books are returned in an array, you can index directly into the array:

PS C:\> $doc.catalog.book[0]


id           : bk101
author       : Gambardella, Matthew
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.



PS C:\>
 

Piping the book element to Get-Member reveals that each book is an instance of the System.Xml.XmlElement .NET Framework class. This is shown here:

PS C:\> $doc.catalog.book[0] | Get-Member


   TypeName: System.Xml.XmlElement

Name                 MemberType            Definition
----                 ----------            ----------
ToString             CodeMethod            static string XmlNode(psobject instance)
AppendChild          Method                System.Xml.XmlNode AppendChild(System....
Clone                Method                System.Xml.XmlNode Clone()
CloneNode            Method                System.Xml.XmlNode CloneNode(bool deep)
CreateNavigator      Method                System.Xml.XPath.XPathNavigator Create...
Equals               Method                bool Equals(System.Object obj)
GetAttribute         Method                string GetAttribute(string name), stri...
GetAttributeNode     Method                System.Xml.XmlAttribute GetAttributeNo...
GetElementsByTagName Method                System.Xml.XmlNodeList GetElementsByTa...
GetEnumerator        Method                System.Collections.IEnumerator GetEnum...
GetHashCode          Method                int GetHashCode()
GetNamespaceOfPrefix Method                string GetNamespaceOfPrefix(string pre...
GetPrefixOfNamespace Method                string GetPrefixOfNamespace(string nam...
GetType              Method                type GetType()
HasAttribute         Method                bool HasAttribute(string name), bool H...
InsertAfter          Method                System.Xml.XmlNode InsertAfter(System....
InsertBefore         Method                System.Xml.XmlNode InsertBefore(System...
Normalize            Method                System.Void Normalize()
PrependChild         Method                System.Xml.XmlNode PrependChild(System...
RemoveAll            Method                System.Void RemoveAll()
RemoveAllAttributes  Method                System.Void RemoveAllAttributes()
RemoveAttribute      Method                System.Void RemoveAttribute(string nam...
RemoveAttributeAt    Method                System.Xml.XmlNode RemoveAttributeAt(i...
RemoveAttributeNode  Method                System.Xml.XmlAttribute RemoveAttribut...
RemoveChild          Method                System.Xml.XmlNode RemoveChild(System....
ReplaceChild         Method                System.Xml.XmlNode ReplaceChild(System...
SelectNodes          Method                System.Xml.XmlNodeList SelectNodes(str...
SelectSingleNode     Method                System.Xml.XmlNode SelectSingleNode(st...
SetAttribute         Method                System.Void SetAttribute(string name, ...
SetAttributeNode     Method                System.Xml.XmlAttribute SetAttributeNo...
Supports             Method                bool Supports(string feature, string v...
WriteContentTo       Method                System.Void WriteContentTo(System.Xml....
WriteTo              Method                System.Void WriteTo(System.Xml.XmlWrit...
Item                 ParameterizedProperty System.Xml.XmlElement Item(string name...
author               Property              System.String author {get;set;}
description          Property              System.String description {get;set;}
genre                Property              System.String genre {get;set;}
id                   Property              System.String id {get;set;}
price                Property              System.String price {get;set;}
publish_date         Property              System.String publish_date {get;set;}
title                Property              System.String title {get;set;}


PS C:\>

The XmlElement class has a clone method that can be used to copy an entire XmlElement and store the copy in a variable. The values associated with each of the attributes of the element can then be modified directly, and the newly created and modified element can be written back to the XmlDocument. A new XmlElement will need to be created for each item that exists in the CSV file. To read the CSV file, use the Import-Csv cmdlet. This portion of the script is shown here:

foreach($e in (Import-Csv -Path $csvPath))

{

 
$element = $Doc.catalog.book[0].clone()


Each attribute of the cloned XmlElement is patched with the appropriate field from the CSV file. A simple assignment operator is used, as shown here:

 
$element.id = $e.id

 
$element.author = $e.author

 
$element.title = $e.title

 
$element.genre = $e.genre

 
$element.price = $e.price

 
$element.publish_date = $e.publish_date

 
$element.description = $e.description

After the attributes of the XmlElement have been filled out from the CSV file, the appendChild method is used to add the data in the newly created XmlElement to the XmlDocument. This is shown here:

$doc.DocumentElement.AppendChild($element)

}

To avoid messing up the original XML file, I save it with a different name:

$doc.Save("C:\fso\x.xml")



The newly created file, x.xml, is shown in the following image.

Image of newly created file in Notepad

 

JS, that is all there is to using Windows PowerShell to write XML information. This also brings to a close our String Week articles. Join us tomorrow for Quick-Hits Friday.

We invite you to follow us on Twitter and Facebook. If you have any questions, send email to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Its interesting script....

    Thanks

  • Hi,

    What do you do if the xml files is empty and you don't have an existing record to use as a template?

    Eg.  The file doesn't have any books in it?

    This is the situation I'm try to solve with Powershell, thanks guys...Gary

  • Gary - if you have a good node the use 'clone' and then appendChild to add the cloned node.  Set the attributes and #text as needed.

  • I'm with Gary. The Clone method is great..... except for when you don't have anything to clone.

  • So, creating a new element with an attribute and some text looks like this....

    $path = "C:\fso\books.xml"

    $csvPath = "C:\fso\books.csv"

    $doc = [xml](Get-Content -Path $path)

    foreach($e in (Import-Csv -Path $csvPath))

    {

    $element = $doc.CreateElement("Book")

    $attribute = $doc.CreateAttribute("ID")

    $element.Attributes.Append($attribute)

    $element.ID = "700"

    $element.InnerText = "This is some #Text"

    }

    $doc.Save("C:\fso\x.xml")

  • This is good. How would I populate existing members of an XML with new attributes from a CSV? Thanks.