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>