Powershell functions to get an xml node, and get and set an xml element’s value, even when the element does not already exist
I’m new to working with Xml through PowerShell and was so impressed when I discovered how easy it was to read an xml element’s value. I’m working with reading/writing .nuspec files for working with NuGet. Here’s a sample xml of a .nuspec xml file:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MyAppsId</id>
<version>1.0.1</version>
<title>MyApp</title>
<authors>Daniel Schroeder</authors>
<owners>Daniel Schroeder</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>My App.</description>
<summary>My App.</summary>
<tags>Powershell, Application</tags>
</metadata>
<files>
<file src="MyApp.ps1" target="content\MyApp.ps1" />
</files>
</package>
In PowerShell if I want to get the version element’s value, I can just do:
# Read in the file contents and return the version node's value.
[ xml ]$fileContents = Get-Content -Path $NuSpecFilePath
return $fileContents.package.metadata.version
Wow, that’s super easy. And if I want to update that version number, I can just do:
# Read in the file contents, update the version node's value, and save the file.
[ xml ] $fileContents = Get-Content -Path $NuSpecFilePath
$fileContents.package.metadata.version = $NewVersionNumber
$fileContents.Save($NuSpecFilePath)
Holy smokes. So simple it blows my mind. So everything is great, right? Well, it is until you try and read or write to an element that doesn’t exist. If the <version>
element is not in the xml, when I try and read from it or write to it, I get an error such as “Error: Property ‘version’ cannot be found on this object. Make sure that it exists.”. You would think that checking if an element exists would be straight-forward and easy right? Well, it almost is. There’s a SelectSingleNode() function that we can use to look for the element, but what I realized after a couple hours of banging my head on the wall and stumbling across this stack overflow post, is that in order for this function to work properly, you really need to use the overloaded method that also takes an XmlNamespaceManager; otherwise the SelectSingleNode() function always returns null.
So basically you need an extra 2 lines in order to setup an XmlNamespaceManager every time you need to look for a node. This is a little painful, so instead I created this function that will get you the node if it exists, and return $null if it doesn’t:
function Get-XmlNode([ xml ]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# If a Namespace URI was not given, use the Xml document's default namespace.
if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }
# In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
$xmlNsManager.AddNamespace("ns", $NamespaceURI)
$fullyQualifiedNodePath = "/ns:$($NodePath.Replace($($NodeSeparatorCharacter), '/ns:'))"
# Try and get the node, then return it. Returns $null if the node was not found.
$node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
return $node
}
And you would call this function like so:
# Read in the file contents and return the version node's value.
[ xml ]$fileContents = Get-Content -Path $NuSpecFilePath
$node = Get-XmlNode -XmlDocument $fileContents -NodePath "package.metadata.version"
if ($node -eq $null) { return $null }
return $fileContents.package.metadata.version
So if the node doesn’t exist (i.e. is $null), I return $null instead of trying to access the non-existent element.
So by default this Get-XmlNode function uses the xml’s root namespace, which is what we want 95% of the time. It also takes a NodeSeparatorCharacter that defaults to a period. While Googling for answers I saw that many people use the the syntax “$fileContents/package/metadata/version” instead of “$fileContents.package.metadata.version”. I prefer the dot notation, but for those who like the slash just override the NodeSeparatorCharacter with a slash.
Update 1
Later I found that I also wanted the ability to return back multiple xml nodes; that is, if multiple “version” elements were defined I wanted to get them all, not just the first one. This is simple; instead of using .SelectSingleNode() we can use .SelectNodes(). In order to avoid duplicating code, I broke the code to get the Xml Namespace Manager and Fully Qualified Node Path out into their own functions. Here is the rewritten code, with the new Get-XmlNodes function:
function Get-XmlNamespaceManager([ xml ]$XmlDocument, [string]$NamespaceURI = "")
{
# If a Namespace URI was not given, use the Xml document's default namespace.
if ([string]::IsNullOrEmpty($NamespaceURI)) { $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI }
# In order for SelectSingleNode() to actually work, we need to use the fully qualified node path along with an Xml Namespace Manager, so set them up.
[System.Xml.XmlNamespaceManager]$xmlNsManager = New-Object System.Xml.XmlNamespaceManager($XmlDocument.NameTable)
$xmlNsManager.AddNamespace("ns", $NamespaceURI)
return ,$xmlNsManager # Need to put the comma before the variable name so that PowerShell doesn't convert it into an Object[].
}
function Get-FullyQualifiedXmlNodePath([string]$NodePath, [string]$NodeSeparatorCharacter = '.')
{
return "/ns:$($NodePath.Replace($($NodeSeparatorCharacter), '/ns:'))"
}
function Get-XmlNode([ xml ]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter
# Try and get the node, then return it. Returns $null if the node was not found.
$node = $XmlDocument.SelectSingleNode($fullyQualifiedNodePath, $xmlNsManager)
return $node
}
function Get-XmlNodes([ xml ]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
$xmlNsManager = Get-XmlNamespaceManager -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI
[string]$fullyQualifiedNodePath = Get-FullyQualifiedXmlNodePath -NodePath $NodePath -NodeSeparatorCharacter $NodeSeparatorCharacter
# Try and get the nodes, then return them. Returns $null if no nodes were found.
$nodes = $XmlDocument.SelectNodes($fullyQualifiedNodePath, $xmlNsManager)
return $nodes
}
Note the comma in the return statement of the Get-XmlNamespaceManager function. It took me a while to discover why things broke without it.
So once I had this, I decided that I might as well make functions for easily getting and setting the text values of an xml element, which is what is provided here:
function Get-XmlElementsTextValue([ xml ]$XmlDocument, [string]$ElementPath, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# Try and get the node.
$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
# If the node already exists, return its value, otherwise return null.
if ($node) { return $node.InnerText } else { return $null }
}
function Set-XmlElementsTextValue([ xml ]$XmlDocument, [string]$ElementPath, [string]$TextValue, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# Try and get the node.
$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
# If the node already exists, update its value.
if ($node)
{
$node.InnerText = $TextValue
}
# Else the node doesn't exist yet, so create it with the given value.
else
{
# Create the new element with the given value.
$elementName = $ElementPath.SubString($ElementPath.LastIndexOf($NodeSeparatorCharacter) + 1)
$element = $XmlDocument.CreateElement($elementName, $XmlDocument.DocumentElement.NamespaceURI)
$textNode = $XmlDocument.CreateTextNode($TextValue)
$element.AppendChild($textNode) > $null
# Try and get the parent node.
$parentNodePath = $ElementPath.SubString(0, $ElementPath.LastIndexOf($NodeSeparatorCharacter))
$parentNode = Get-XmlNode -XmlDocument $XmlDocument -NodePath $parentNodePath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
if ($parentNode)
{
$parentNode.AppendChild($element) > $null
}
else
{
throw "$parentNodePath does not exist in the xml."
}
}
}
The Get-XmlElementsTextValue function is pretty straight forward; return the value if it exists, otherwise return null. The Set-XmlElementsTextValue is a little more involved because if the element does not exist already, we need to create the new element and attach it as a child to the parent element.
Here’s an example of calling Get-XmlElementsTextValue:
# Read in the file contents and return the version element's value.
[ xml ]$fileContents = Get-Content -Path $NuSpecFilePath
return Get-XmlElementsTextValue -XmlDocument $fileContents -ElementPath "package.metadata.version"
And an example of calling Set-XmlElementsTextValue:
# Read in the file contents, update the version element's value, and save the file.
[ xml ]$fileContents = Get-Content -Path $NuSpecFilePath
Set-XmlElementsTextValue -XmlDocument $fileContents -ElementPath "package.metadata.version" -TextValue $NewVersionNumber
$fileContents.Save($NuSpecFilePath)
Note that these 2 functions depend on the Get-XmlNode function provided above.
Update 2 - January 7, 2016
I have had multiple people ask me for similar functions for getting and setting an element’s Attribute value as well, so here are the corresponding functions for that:
function Get-XmlElementsAttributeValue([ xml ]$XmlDocument, [string]$ElementPath, [string]$AttributeName, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# Try and get the node.
$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
# If the node and attribute already exist, return the attribute's value, otherwise return null.
if ($node -and $node.$AttributeName) { return $node.$AttributeName } else { return $null }
}
function Set-XmlElementsAttributeValue([ xml ]$XmlDocument, [string]$ElementPath, [string]$AttributeName, [string]$AttributeValue, [string]$NamespaceURI = "", [string]$NodeSeparatorCharacter = '.')
{
# Try and get the node.
$node = Get-XmlNode -XmlDocument $XmlDocument -NodePath $ElementPath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
# If the node already exists, create/update its attribute's value.
if ($node)
{
$attribute = $XmlDocument.CreateNode([System.Xml.XmlNodeType]::Attribute, $AttributeName, $NamespaceURI)
$attribute.Value = $AttributeValue
$node.Attributes.SetNamedItem($attribute) > $null
}
# Else the node doesn't exist yet, so create it with the given attribute value.
else
{
# Create the new element with the given value.
$elementName = $ElementPath.SubString($ElementPath.LastIndexOf($NodeSeparatorCharacter) + 1)
$element = $XmlDocument.CreateElement($elementName, $XmlDocument.DocumentElement.NamespaceURI)
$element.SetAttribute($AttributeName, $NamespaceURI, $AttributeValue) > $null
# Try and get the parent node.
$parentNodePath = $ElementPath.SubString(0, $ElementPath.LastIndexOf($NodeSeparatorCharacter))
$parentNode = Get-XmlNode -XmlDocument $XmlDocument -NodePath $parentNodePath -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter
if ($parentNode)
{
$parentNode.AppendChild($element) > $null
}
else
{
throw "$parentNodePath does not exist in the xml."
}
}
}
Rather than copy-pasting, you can download all of the functions shown here.
I hope you find this useful and that it saves you some time. Happy coding!
Comments
Carl
Thanks for the article and code samples. Much appreciated as I was coming across the error described when checking for the existence of an XML element.
Your code works well and has saved me some time.
One small point, I think you have some typos in the code block. The [xml][/xml] parameter type in the function headers appears to have become a number 1”
e.g.
function Get-XmlNode(1$XmlDocument, [string]$NodePath, [string]$NamespaceURI = “”, [string]$NodeSeparatorCharacter = ‘.’) {… }
deadlydog
@Carl Thanks Carl, WordPress was just parsing my code improperly and converting it to ‘1’. I’ve updated the code to put spaces around the word xml. All fixed now.
john mcgregor
Hello,
Thanks for this post - I’ve happily plundered your code :-)
I was wondering if you would be able to add to it and show how to read/write to a SharePoint library of xml files. As far as I know the get-content won’t work. I’ve been trying SPFile.OpenBinary() and OpenBinaryStream() which have read ok, but still no luck saving. My situation is concerns a library of InfoPath form data - stored as xml - which I need to bulk amend.
Thanks, John
john mcgregor
Hello again,
I figured out something that works. Not sure if its the best way or not, but it works:
$stream = $file.OpenBinaryStream() $xml = New-Object System.Xml.XmlDocument $xml.Load($stream) # #do stuff to $xml using your functions - thanks # $xml.Save($stream) $outStream = [System.Text.ASCIIEncoding]::ASCII.GetBytes($xml.InnerXml) $file.SaveBinary($outStream)
Thanks again for the post, John
Vanessa
Thank you very much for this. Saved a lot of frustration and time.
Sambhaji
Thanks ! It helped me lot!
David
Excellent Blog….this is just the information I needed. I was trying to figure out how to dynamically determine the namespace of an xml file so I could then include it in my xpath for pulling data….couldn’t hard code because I needed to rotate through thousands of xmls with different namespaces. This information was perfect for me and worked. All I really needed to finish my program was this for pulling the default namespace: $NamespaceURI = $XmlDocument.DocumentElement.NamespaceURI
Thanks again!!!
John
How about getting a list of all nodes and all children? I’m the type that would like to see that ‘package’ and/or ‘metadata’ is a node and ‘ID’ is a child of ‘metadata’ but not ‘file’ or ‘package’ and can feed get/set XmlElementsTextValue the right parameters.
Ed
Thanks, really helped on small project and learning more on Posh
Charles
Hi,
Thanks for the code example - it is helpful. I am struggling to figure out how to Get and Set attributes using this code as the basis. I have been trying to adapt the Get-XmlElementsTextValue and the Set-XmlElementsTextValue functions to make new functions Get-XmlElementAttributesValue and Set-XmlElementAttributesValue with no success. I am thinking that I should be able to use the $node to select the attribute. That has not been successful.
How can I get to the password attribute of a xml path of configuration.servers.server? In the example below I want to change the password attribute value.
deadlydog
@Charles Hi Charles, I’ve just updated this post to include functions for getting and setting an attribute’s value as well. Sorry it took so long :P Enjoy!
Charles
@deadlydog - Thanks and wonderful surprise in my inbox today!! I will circle back to what I did on that project and see how I can improve it with this code. Thank You!
dragon788
This is pretty awesome. I was just trying to figure out how to query the version from a nuspec file to append a build number in my AppVeyor config for a Chocolatey package and after some failures with Select-Xml I ran across your awesome post.
Renzo
Hello, I have an XML file that includes comments (much more readable). However this is causing havoc with my processing logic.
Part of the logic uses $SequenceCount = ($CFG.APP.INSTALL.ChildNodes).Count.
I either need to DELETE comments from the XML object or, alternatively, I need the count to EXCLUDE comments. Can you help??
Renzo
Found the answer! ($Test.App.ChildNodes |Where-Object { ‘#comment’ -contains $.Name }) | ForEach-Object { # Remove each node from its parent [void]$.ParentNode.RemoveChild($_)
R
Totally copying (and giving credit to) your script! Just saved me reinventing the wheel, appreciated! Great work and thanks for sharing
Gary
I use your XML functions all of the time and they are great, but I hit a wall. Any idea how to parse an Excel XML file?
[xml]$xmlInvoice = Get-Content $x.Fullname $dataNodes = Get-XmlNodes -XmlDocument $xmlInvoice -NodePath “ss:Workbook.ss:Worksheet.ss:Table.ss:Row.ss:Cell.ss:Data” ForEach ( $node in $dataNodes ) {
}
Exception calling “SelectNodes” with “2” argument(s): “‘/ns:ss:Workbook/ns:ss:Worksheet/ns:ss:Table/ns:ss:Row/ns:ss:Cell/ns:ss:Data’ has an invalid token.” At H:\My Documents\Quality Assurance\STPr In Progress\Foodservice Pro Points\Invoice Data Processing\External-Excel-Processing.ps1:33 char:2
Mitch
Thank you. Excellent blog and XML function library. I’ll be using it today!
Me
Thanks a ton!
Dan
Get-XmlElementsAttributeValue -XmlDocument $fileContents -ElementPath “package.metadata.version” -AttributeName “djiversion” Set-XmlElementsAttributeValue -XmlDocument $fileContents -ElementPath “package.metadata.version” -AttributeName “djiversion” -AttributeValue “1.0.0”
I notice that the Set-XmlElementsAttributesValue just adds the name and value, but it doesn’t add the new line, or the less than or greater than, or the end of the tag.
Dan
’
Dan
Steven
Hello,
Many thanks for this job ! It helps me so much. Based on your function, i’m using Set-XmlElementsAttributeValue. for example :
$XMLfile = “F:\Deploy\Control\toto.xml”
Use Get-Content to load the ts.xml file as a XML object
[ xml ]$XMLContent = Get-Content -Path $XMLfile
Get-XmlNode -XmlDocument $XMLContent -NodePath “step”
Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “type” -AttributeValue “SMS_TaskSequence_SetVariableAction” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “name” -AttributeValue “[SG] - Set OSDisk” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “description” -AttributeValue “For Preload OEM TS” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “disable” -AttributeValue “false” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “successCodeList” -AttributeValue “0 3010” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList.variable” -AttributeName “name” -AttributeValue “VariableName” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList.variable” -AttributeName “property” -AttributeValue “VariableName” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList”
but in my case, i’m working with MDT and the TS.xml looks like bellow :
I trying to use your function to solve my problem, but no result. 2 ElementPath are Variable.
Then i would like to “InsertBefore” or “InsertAfter” my node step between other node. I have not already take a look on it. i keep you inform. OSDisk C:
If i run the function the first line will be overwrite by the second. Do you have an idea ?
Steven
Hello,
Many thanks for this job ! It helps me so much. Based on your function, i’m using Set-XmlElementsAttributeValue. for example :
$XMLfile = “F:\Deploy\Control\toto.xml”
Use Get-Content to load the ts.xml file as a XML object
[ xml ]$XMLContent = Get-Content -Path $XMLfile
Get-XmlNode -XmlDocument $XMLContent -NodePath “step”
Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “type” -AttributeValue “SMS_TaskSequence_SetVariableAction” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “name” -AttributeValue “[SG] – Set OSDisk” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “description” -AttributeValue “For Preload OEM TS” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “disable” -AttributeValue “false” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step” -AttributeName “successCodeList” -AttributeValue “0 3010” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList.variable” -AttributeName “name” -AttributeValue “VariableName” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList.variable” -AttributeName “property” -AttributeValue “VariableName” Set-XmlElementsAttributeValue -XmlDocument $XMLContent -ElementPath “package.step.defaultVarList”
but in my case, i’m working with MDT and the TS.xml looks like bellow :
<!– –> <!– –> <!– OSDisk –> <!– C: –> <!– –> <!– cscript.exe “%SCRIPTROOT%\ZTISetVariable.wsf” –> <!– –>
I trying to use your function to solve my problem, but no result. 2 ElementPath are Variable. <!– OSDisk –> <!– C: –>
If i run the function the first line will be overwrite by the second. Do you have an idea ?
Then i would like to “InsertBefore” or “InsertAfter” my node step between other nodes. I have not already take a look on it. i keep you inform.
Steven
Sorry i’m not able to show you the xml …
SS
If there are multiple metadata nodes, how do i find the one that i wanted ?
Dev
Hi Daniel, thank you for this work! I encountered an issue with Set-XmlElementsAttributeValue, not able to solve it, yet. Basically this is the XML:
I tried this:
Set-XmlElementsAttributeValue -XmlDocument $Xml -ElementPath "Config.SubNode" -AttributeName 'Hello' AttributeValue '0';
I traced the issue down to function Get-XmlNodes, where this returns null, but only in case where the node to change is too close to the root node:
$nodes = $XmlDocument.SelectNodes($fullyQualifiedNodePath, $xmlNsManager)
Donlassini
This is great :-) I added the code below to allow fast creation of an XML node including the path up to the root if need be. I use the verb “Register-“ for functions that retrieve an object, and if it doesn’t exist, the function will create it and return it. So if the note exists, you will get it - and if it doesn’t exist, it will be created, and then you’ll get it.
Function Get-Parent { Param ( [string]$Path, [string]$SeparatorCharacter ) [string[]]$Elements = $Path.Split($SeparatorCharacter) Switch ($Elements.Count) { 0 { “” } 1 { “” } Default { ($Elements | Select-Object -First ($Elements.Count - 1)) -Join $SeparatorCharacter } } }
function Register-XmlNode([xml]$XmlDocument, [string]$NodePath, [string]$NamespaceURI = “”, [string]$NodeSeparatorCharacter = ‘.’) { If ($NodePath -eq “”) { Throw “Path is invalid.” } $XmlNode = Get-XmlNode @PSBoundParameters If ($XmlNode) { $XmlNode } Else { $Parent = Register-XmlNode -NodePath (Get-Parent -Path $NodePath -SeparatorCharacter $NodeSeparatorCharacter) -XmlDocument $XmlDocument -NamespaceURI $NamespaceURI -NodeSeparatorCharacter $NodeSeparatorCharacter If ($Parent) { $ChildNode = $XmlDocument.CreateNode(‘element’, ($NodePath.Split($NodeSeparatorCharacter) | Select-Object -Last 1), ‘’) $Parent.AppendChild($ChildNode) } } }
Kenneth Shaw
I am new to PowerShell and have been looking for a way to parse and compare 2 xml files. Some of the complex XML has repeatable fields that I need to be put in the same order in both files. I started to play with your code to see if I can use parts of it to do the ordering and comparing.
Ken Shaw
When passing the XML to the functions I get this error: Get-XmlNodes : Cannot process argument transformation on parameter ‘XmlDocument’. Cannot convert value “System.Object[]” to type “System.Xml.XmlDocument”. Error: “The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type.” At C:\Users\OrderFile.ps1:154 char:19
I have googled the error and tried multiple ways to force the object to XML. Is anyone else having this issue?
Ken Shat
I got most of your functions to work but for some reason I am get nulls when I try to .SelectNode or .SelectNodes. I know the file has them in there but I am a bit confused about the name space. Any help from anyone would be great.
Leave a Comment
Your email address will not be published. Required fields are marked *