55

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'.

Warlock
  • 7,321
  • 10
  • 55
  • 75

1 Answers1

89

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.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • Correct code is $doc = [xml] (Get-Content $filePath) It doesn't resolve my problem – Warlock Oct 08 '13 at 10:45
  • 2
    I found that if 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. – Warlock Oct 08 '13 at 11:00
  • 13
    I hate it when people try to be clever with their software and all they end up doing is making your life more difficult. – jpmc26 Mar 18 '15 at 20:22
  • A whole day and counting lost because powershell has to mutate types while i'm in the middle of creating and removing xml elements. – StingyJack Jan 20 '18 at 20:21
  • @StingyJack You are not forced to use the dot notation to navigate the file. It's a convenience facility, and thus a compromise. It works well for some XML structures and not so well for others (even though I would tend to say, it works better for the sane ones and not so well for the not so sane ones, but that's highly subjective). In any case if you want to be precise and definite, use XPath or regular DOM methods instead of the shorthand syntax. – Tomalak Jan 20 '18 at 20:53
  • 5
    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. – StingyJack Jan 20 '18 at 22:54
  • The convenience is in *reading* XML files easily, for example config files. If you want to modify a file, use the DOM methods or XPath, never the shorthand syntax. It has not been made for this kind of work. If XML files were *truly* fully navigable by a dot path syntax without any compromises and shortcomings, we would not even have the much more complex XPath. Of course we can argue whether it's fundamentally useful to even have a kind of syntax that only covers a part of what XML can express, but that is a different matter altogether. – Tomalak Jan 21 '18 at 08:16
  • 1
    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. – zett42 Oct 29 '20 at 17:18