Home > database >  Appending multiplelines or "nodes" to an XML document correctly
Appending multiplelines or "nodes" to an XML document correctly

Time:12-18

Lets say I have an .xml that currently looks like this:

<?xml version="1.0"?>
<button backcol="none" display="label" label_pos="right" separate="yes" textcol="none">
  <label>MultiLine</label>
  <tip>Opens an Explorer window for the current folder</tip>
  <icon1>#folder</icon1>
  <function type="normal">
    <instruction>Hello 0</instruction>
  </function>
</button>

I would like to use Powershell to replace any "instruction" nodes the input has, so an output of the above xml could look like:

<?xml version="1.0"?>
<button backcol="none" display="label" label_pos="right" separate="yes" textcol="none">
  <label>MultiLine</label>
  <tip>Opens an Explorer window for the current folder</tip>
  <icon1>#folder</icon1>
  <function type="normal">
    <instruction>Hello 1</instruction>
    <instruction>Hello 2</instruction>
    <instruction>Hello 3</instruction>
  </function>
</button>

I wrote a function to achieve this:


function DopusML {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline)]
        $InputObject
      )

    Process{
        $OutXML    = "C:\Temp\MultiLine.dcf"
        $MultiLine = [xml](Get-Content "C:\Temp\MultiLine.dcf")
    
        $InputObject -split "\n" | foreach-object {
            $CurrentLine = $_
            select-Xml -Xml $MultiLine -XPath "//button[label='MultiLine']//instruction" | % {$_.Node.InnerXml = "$CurrentLine"}
        }
        $MultiLine.Save($OutXML)
    }
}

Doing Something like:

"Hello 1
Hello 2
Hello 3" | DopusML

Gives me the output:

<?xml version="1.0"?>
<button backcol="none" display="label" label_pos="right" separate="yes" textcol="none">
  <label>MultiLine</label>
  <tip>Opens an Explorer window for the current folder</tip>
  <icon1>#folder</icon1>
  <function type="normal">
    <instruction>Hello 3</instruction>
  </function>
</button>

Meaning only the last line is saved, instead of all the lines I thing my propblem lies at:

select-Xml -Xml $MultiLine -XPath "//button[label='MultiLine']//instruction" | % {$_.Node.InnerXml = "$CurrentLine"}

so I tried:

select-Xml -Xml $MultiLine -XPath "//button[label='MultiLine']//instruction" | % {$_.Node.InnerXml = "$CurrentLine"}

As well as:

select-Xml -Xml $MultiLine -XPath "//button[label='MultiLine']//instruction" | % {$_.Node.InnerXml = @($CurrentLine)}


The articles I have been reading dont really go into depth into this issue and am out of idea as well.

Any help or ideas, would be really appreciated!

CodePudding user response:

Edit

As per your comment, I have added a switch -Append to the function.
without specifying this when calling the function, all already present nodes will first be removed.
with specifying switch -Append when calling the function, new nodes will be added, leaving already present nodes untouched

function DopusML {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeline = $true)]
        [string]$InputObject,

        [switch]$Append
    )

    $file = "D:\Test\MultiLine.dcf"  # should this be hardcoded?
    # load the xml file. This way, you are ensured to get the file encoding correct
    $xml = [System.Xml.XmlDocument]::new()
    $xml.Load($file)

    if (!$Append) {
        # you want to first remove the already present instruction nodes
        $xml.DocumentElement.function.ChildNodes | Sort-Object -Descending | ForEach-Object {
            [void]$_.ParentNode.RemoveChild($_)
        }
    }
    foreach ($string in ($InputObject -split '\r?\n')) {
        $newNode = $xml.CreateElement('instruction')
        $newNode.InnerText = $string
        [void]$xml.DocumentElement.function.AppendChild($newNode)
    }

    $xml.Save($file)
}

Then calling it with

@"
Hello 1
Hello 2
Hello 3
"@ | DopusML

would result in

<?xml version="1.0"?>
<button backcol="none" display="label" label_pos="right" separate="yes" textcol="none">
  <label>MultiLine</label>
  <tip>Opens an Explorer window for the current folder</tip>
  <icon1>#folder</icon1>
  <function type="normal">
    <instruction>Hello 1</instruction>
    <instruction>Hello 2</instruction>
    <instruction>Hello 3</instruction>
  </function>
</button>
  • Related