1

I have been searching through multiple explanations, but I don't seem to be able to find a solution.

My XML looks like this:

<?xml version="1.0" encoding="utf-8"?>
<ConfigRightCollection>
  <ConfigRight Name="CreateSampling">
    <GroupCollection>
      <Group>All</Group>
    </GroupCollection>
  </ConfigRight>
  <ConfigRight Name="InputMeasure">
    <GroupCollection>
      <Group>All</Group>
    </GroupCollection>
  </ConfigRight>
  <ConfigRight Name="AllowCreateOnStoppedJob">
    <GroupCollection>
      <Group>Admin</Group>
    </GroupCollection>
  </ConfigRight>
</ConfigRightCollection>

I want to add to new elements to the 'ConfigRight Name = "AllowCreateOnStoppedJob' group so that it looks like this:

<?xml version="1.0" encoding="utf-8"?>
<ConfigRightCollection>
  <ConfigRight Name="CreateSampling">
    <GroupCollection>
      <Group>All</Group>
    </GroupCollection>
  </ConfigRight>
  <ConfigRight Name="InputMeasure">
    <GroupCollection>
      <Group>All</Group>
    </GroupCollection>
  </ConfigRight>
  <ConfigRight Name="AllowCreateOnStoppedJob">
    <GroupCollection>
      <Group>Admin</Group>
      <Group>QS</Group>
      <Group>QU</Group>
    </GroupCollection>
  </ConfigRight>
</ConfigRightCollection>

My code so far that seems to work:

$filePath = "\\some\path\to\the\file.xml"
[xml]$xmlData = Get-Content -Path $filePath
$xmlData | Select-Xml -XPath '/ConfigRightCollection/ConfigRight' | Where-Object{ ($_.Node.Name -eq "AllowCreateOnStoppedJob") } | Select-Object -ExpandProperty "Node"

But the code to actually add the new elements does not work, I receive the error "cannot call a method on a null-valued expression":

$newGroups = "QS","QU"
ForEach ($group in $newGroups)
{
     # Creation of a sub node
     $xmlSubElt = $xmlData.GroupCollection.CreateElement("Group")
     $xmlSubText = $xmlData.GroupCollection.CreateTextNode($group)
     $xmlSubElt.AppendChild($xmlSubText)
}
$xmlData.Save

The part that I cannot seem to resolve is how do I select the correct ConfigRight element and add 2 new elements to its GroupCollection sub-element? I am limited to PS 5.1

ZZ51f9
  • 19
  • 2

2 Answers2

0

Using Xml Linq

using assembly System.Xml.Linq

$inputFilename = "c:\temp\test.xml"
$outputFilename = "c:\temp\tes1.xml"


$doc = [System.Xml.Linq.XDocument]::Load($inputFilename)

$configRight = $doc.Descendants("ConfigRight")
$allowCreateOnStoppedJob = [System.Linq.Enumerable]::Where($configRight, [Func[object,bool]]{ param($x) [string]$x.Attribute("Name").Value -eq "AllowCreateOnStoppedJob"})[0]

$groupCollection = $allowCreateOnStoppedJob.Element('GroupCollection')

$group = [System.Xml.Linq.XElement]::new([System.Xml.Linq.XName]::Get('Group'),'QS')
$groupCollection.Add($group)

$group = [System.Xml.Linq.XElement]::new([System.Xml.Linq.XName]::Get('Group'),'QU')
$groupCollection.Add($group)

$doc.Save($outputFilename)
jdweng
  • 33,250
  • 2
  • 15
  • 20
0
# The input XML file's full path.
# Note: Convert-Path ensures that the path is a *full* (absolute)
#       one, which is required for passing file-system paths to .NET methods,
#       given that .NET's working dir. usually differs from PowerShell's.
$filePath = Convert-Path -LiteralPath "\\some\path\to\the\file.xml"

# Get the parent element of interest with a single XPath query.
$element = (
  Select-Xml -LiteralPath $filePath '/ConfigRightCollection/ConfigRight[@Name="AllowCreateOnStoppedJob"]/GroupCollection'
).Node

# Get a reference to the owning document.
$doc = $element.OwnerDocument

# Append the child elements.
foreach ($newGroup in 'QS', 'QU') {
  ($element.AppendChild(
    $doc.CreateElement('Group')
  )).InnerText = $newGroup
}

# Save back to the input file.
$doc.Save($filePath)

As for what you tried:

  • [xml]$xmlData = Get-Content -Path $filePath is a convenient, but not robust way to parse an XML file - see the bottom section of this answer.

  • However, you don't need to parse the XML file separately first, given that Select-Xml can itself parse an XML file, via its -LiteralPath (and -Path) parameter, as shown above.

  • $xmlData | Select-Xml -XPath '/ConfigRightCollection/ConfigRight' | Where-Object{ ($_.Node.Name -eq "AllowCreateOnStoppedJob") } | Select-Object -ExpandProperty "Node" simply outputs the result of your command, it does not modify $xmlData in place - you'd have to assign the result to a (new) variable to capture it, as shown above.

  • Because $xmlData didn't actually contain the result of your Select-Xml-based query but the original document, $xmlData.GroupCollection evaluated to $null, given that the document element itself has no <GroupCollection> child element, and trying to call a method (such as .CreatElement()) on $null invariably results in the error you saw.

mklement0
  • 382,024
  • 64
  • 607
  • 775