xmlpowershellspecial-charactersxmldocumentxmlelement

Escaping colon in PowerShell XmlDocument.CreateElement


I am trying to escape a colon in the CreateElement method. So for example I want to change the following:

$node = $xmldoc.CreateElement("test:example")

To give me a node that looks like this:

<test:example>

But I unfortunately only get this:

<example>

The Microsoft documentation on this method states that anything preceding a colon gets used as the prefix property and the rest is the LocalName. The prefix doesn't appear in the node, so I want to do a workaround that outputs the colon but doesn't classify the first part as the prefix.

I tried using a baskslash to escape it and generally looked for different ways to do it on other threads, but oftentimes the answer is to avoid the situation where you have to escape the character in the first place. I can't avoid it though because I have no say in the overall expected XML structure.


Solution

  • Preface:


    Martin Honnen has provided the crucial pointer:

    If you use a namespace prefix in your element name, you must provide the corresponding namespace URI to the System.Xml.XmlDocument.CreateElement() call, as the second argument:

    $node = $xmldoc.CreateElement('test:example', 'https://example.org')
    

    Alternatively, use the overload where you specify the prefix separately:

    $node = $xmldoc.CreateElement('test', 'example', 'https://example.org')
    

    Note:


    A self-contained example, focused on elements only:

    # Create a sample document that declares a 'test' same prefix with
    # a sample namespace URI.
    $sampleNamespaceUri = 'https://example.org'
    $doc = [xml] "<foo xmlns:test=`"$sampleNamespaceUri`"/>"
    
    # Create the new element.
    # NOTE:
    #   * Even though the owner document knows prefix 'test', you must still
    #     pass the corresponding namespace URI explcitly.
    #   * If the URI *differs* from the one associated with 'test' in the owner
    #     document, the prefix will be *redefined as part your element*, i.e.
    #     a 'xmlns:test="<different-URI>' attribute will be added to it.
    $node = $doc.CreateElement('test:me', $sampleNamespaceUri)
    
    # Append the element as a child node.
    $null = $doc.foo.AppendChild($node)
    
    # Print the modified document's XML text.
    $doc.OuterXml
    

    Output (prettied):

    <foo xmlns:test="https://example.org">
      <test:me />
    </foo>
    

    Note: If the namespace URIs hadn't matched, you'd see something like the following - note the redefinition of the prefix at the level of the newly inserted element:

    <foo xmlns:test="https://example.org">
      <test:me xmlns:test="https://different.example.org" />
    </foo>