57

I'm trying to create a child XML element for this xml:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>

I use this PowerShell script:

[xml] $doc = Get-Content($filePath)
$child = $doc.CreateElement("newElement")
$doc.configuration.AppendChild($child)

I have an error: Method invocation failed because [System.String] doesn't contain a method named 'AppendChild'.

2
  • This has got to be the single dumbest "quirk" in powershell. Commented Jan 20, 2018 at 20:22
  • This works fine in powershell 6. (Tested on 6.2) Commented Apr 30, 2019 at 14:45

1 Answer 1

91

If you use dot notation to navigate an XML file (e.g. $doc.configuration), Powershell tries to be clever about what it returns.

  • If the target element is empty or only contains a single text node, PS will return a String.
  • If the target element contains child nodes other than text nodes, it will return an XmlElement.
  • If multiple target elements exist, it will return an Object[], where each individual array element is again subject to these rules, e.g. it will either be a String or an XmlElement depending on its contents.
  • If the target element does not exist, PS returns $null.

In your case it's easy since you want to append nodes to the document element:

$doc = New-Object System.Xml.XmlDocument
$doc.Load($filePath)
$child = $doc.CreateElement("newElement")
$doc.DocumentElement.AppendChild($child)

but you could use $doc.SelectNodes() or $doc.SelectSingleNode() to navigate around the XML document and always have a node/node list returned.


One could argue about the sensibility of this behavior, but as a matter of fact it makes consuming (sanely structured) XML quite straight-forward - for example tasks such as reading values from a config file, or from an API response. That's the purpose of this simple syntax.

It's not a good tool for creating XML, which is a more complex task. Using DOM API methods from the start is the better approach here.

Sign up to request clarification or add additional context in comments.

8 Comments

Correct code is $doc = [xml] (Get-Content $filePath) It doesn't resolve my problem
I found that if <configuration> tag is empty, $doc.configuration is String, if not empty - XmlElement. I've solved my problem using $doc.SelectSingleNode('configuration') that returns $null or XmlElement.
I hate it when people try to be clever with their software and all they end up doing is making your life more difficult.
I think its reasonable to expect the XmlElement instance that i just iterated children of and removed said children from would not mutate type to a String for the next statement when I attempt to append a new child element. I'm not sure I understand the convenience to be had in that kind of mutation.
The last two lines can be comined like this: $child = $doc.DocumentElement.AppendChild( $doc.CreateElement("newElement") ). This also prevents PS from writing the return value of AppendChild to the output.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.