Save XML does not format new nodes when formatting preservewhitespace

Viewed 507

Got a large XML with lots of whitespace formatting. I need to add some nodes to the XML. I can load the XML with $xml.PreserveWhiteSpace = $true just fine and then later save the XML file which preserves the white space loaded.

However, if I add a new node to the loaded XML (with .AppendChild), this new node gets written all on 1 line when saving the file.

If I turn off PreserveWhiteSpace when loading the source file, my new node gets correctly saved with white space and indentation. However, all original file white space is lost.

How can I load XML with PreserveWhiteSpace AND also add new nodes with the new nodes have proper new lines for each element?

Load code:

$xmlitems = New-Object Xml
$xmlitems.PreserveWhitespace = $true
$xmlitems.Load($7items_xml)

Here is part of the add new node (element) code:

$newrec = $xmlitems.CreateElement("item")
$xmlitems.Items.AppendChild($newrec) | Out-Null
$newrec.SetAttribute("id", $maxid)
$newrec.SetAttribute("name", "plantFibertankTop")

saveXml $xmlitems $7items_xml

Save code - $xmlitems and $7items_xml (file name) is passed into this function:

function saveXml([xml]$xml_data,[string]$xml_file){
    Write-Host "Saving $xml_file"
    $enc = New-Object System.Text.UTF8Encoding($false)
    $wrt = New-Object System.XML.XMLTextWriter($xml_file, $enc)
    $wrt.Formatting = 'Indented'
    $wrt.Indentation = 4
    $xml_data.Save($wrt)
    $wrt.Close()
}

Note: This XML input and create element is all valid XML and works correctly, except the formatting.

To repeat and summarize: How can I preserve whitespace in the loaded XML file and also get indentations/formatting for any new nodes/elements I add? My testing leads me to believe I can only have 1, not both.

1 Answers

The best i have got till now is to format the xml twice, having the common code for formatting xml:

function script:Write-Xml {
  param(
    [Parameter(Mandatory=$true)][xml]$Xml,
    [Parameter(Mandatory=$false)][Switch]$InternalRepeat
  )

  $private:stringbuilder = New-Object System.Text.StringBuilder
  $private:settings = (New-Object System.Xml.XmlWriterSettings)
  $settings.Indent = $true
  $settings.IndentChars = "  "
  $settings.NewLineChars = "`n"

  $xmlWriter = [System.XML.XmlWriter]::Create($stringbuilder, $settings)
  $Xml.WriteContentTo($xmlWriter)
  $xmlWriter.Flush()
  $xmlWriter.Close()

  if (!$InternalRepeat.IsPresent) {
    return (Write-Xml -Xml $stringbuilder.ToString() -InternalRepeat)
  }
  return $stringbuilder.ToString()
}

And using it like this, will do the job:

Write-Xml -Xml $xmlDoc

i have no idea how i came to the idea, nor why it works, but at least on PS 5.1, it does work ;)

Related