M.Strange
M.Strange

Reputation: 23

Creating XML Elements in PowerShell that inherit namespace prefix from parent node

I'm trying to create an xml document using PowerShell, my issue is that the elements that have been appended to a parent, do not inherit the declare namespace prefix, however whilst playing around, I have managed to get it have the prefix but only with it including another xmlns="".

$UserName = $env:UserName
$OutputPath = "C:\Users\" + $UserName +"\Desktop\Generator\Output\"
$OutputXML = $OutputPath + "Test_File_1.xml"
#Namespace URIs
$VQ5NSX = "http://example2"
$VQ5INTX ="http://example1"
[xml]$Doc = New-Object System.Xml.XmlDocument
$dec = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null)
$doc.AppendChild($dec)

    $root = $doc.CreateNode("element","ns:responsefile", $VQ5NSX)

        $int = $doc.CreateNode("element", "int:IntegrationHeader",$VQ5INTX)

            $IntDt = $doc.CreateElement("int:Date",$null)
                $IntDt.InnerText = "Date Text"
            $Int.AppendChild($IntDt) | Out-Null

            $IntId = $doc.CreateElement("Identification")

                $IntAppID = $doc.CreateElement("ApplicationId")
                    $IntAppIDtxt = $Doc.CreateTextNode("App Id Text")
                $IntAppId.AppendChild($IntAppIDtxt)|Out-Null
            $IntId.AppendChild($IntAppID)|Out-Null

                $IntTransID = $doc.CreateElement("TransactionId")
                    $IntTransIDtxt = $Doc.CreateTextNode("Tran Id Text")
                $IntTransID.AppendChild($IntTransIDtxt) |Out-Null
            $IntId.AppendChild($IntTransID) |Out-Null

            $Int.AppendChild($IntId) |Out-Null
        $root.AppendChild($int)|Out-Null

        $Res = $doc.CreateNode("element","ns:ResponseItem", $null)
            $ItemDetail = $doc.CreateElement("ns:Item1")
                $ItemDetail = $doc.CreateTextNode("Item one text")

            $Res.AppendChild($ItemDetail)|Out-Null
        $root.AppendChild($res)|Out-Null

    $doc.AppendChild($root) |Out-Null  
$doc.save($OutputXML)

This generates the following output;

<?xml version="1.0" encoding="UTF-8"?>
<ns:responsefile xmlns:ns="http://example2">
  <int:IntegrationHeader xmlns:int="http://example1">
    <Date>Date Text</Date>
    <Identification>
      <ApplicationId>App Id Text</ApplicationId>
      <TransactionId>Tran Id Text</TransactionId>
    </Identification>
  </int:IntegrationHeader>
  <ResponseItem>Item one text</ResponseItem>
</ns:responsefile>

However I need it to generate this output;

<?xml version="1.0" encoding="UTF-8"?>
<ns:responsefile xmlns:ns="http://example2">
  <int:IntegrationHeader xmlns:int="http://example1">
    <int:Date>Date Text</Date>
    <int:Identification>
      <int:ApplicationId>App Id Text</int:ApplicationId>
      <int:TransactionId>Tran Id Text</int:TransactionId>
    </int:Identification>
  </int:IntegrationHeader>
  <ns:ResponseItem>Item one text</ns:ResponseItem>
</ns:responsefile>

Any help would be most appreciated!

Upvotes: 2

Views: 2073

Answers (2)

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200193

For working with namespaced XML use a namespace manager.

[xml]$doc = @'
<?xml version="1.0" encoding="UTF-8"?>
<ns:responsefile xmlns:ns="http://example2">
  <int:IntegrationHeader xmlns:int="http://example1">
  </int:IntegrationHeader>
  <ResponseItem>Item one text</ResponseItem>
</ns:responsefile>
'@

$int_ns = 'http://example1'

$nsm = New-Object Xml.XmlNamespaceManager($doc.NameTable)
$nsm.AddNamespace('int', $int_ns)

Create the new element in the intended namespace before appending it to the parent node:

$Int = $doc.SelectSingleNode('//int:IntegrationHeader', $nsm)

$IntDt = $doc.CreateElement('int', 'Date', $int_ns)
$IntDt.InnerText = "Date Text"
$Int.AppendChild($IntDt) | Out-Null

Upvotes: 1

IMSoP
IMSoP

Reputation: 97658

Although the definition of the prefix can be inherited by the child nodes, each child node still needs to be specified as in that namespace.

Currently, you are explicitly requesting that the <Date> element is not in any namespace:

$doc.CreateElement("int:Date",$null)

The .net class is assuming you put the int: in by mistake, because an element in no namespace has no prefix.

Instead, you need to specify that the <Date> element is in the http://example1 namespace:

$doc.CreateElement("int:Date",$VQ5INTX)

If the implementation is smart, it will notice that the prefix int is already bound to that namespace, and avoid repeating the xmlns:int attribute (which would be untidy, but not change the document significantly).

Upvotes: 1

Related Questions