Home > Software engineering >  Traverse into xml nodes using PowerShell
Traverse into xml nodes using PowerShell

Time:08-24

In this xml, i have to filter tag <controlTask> id values which matches to my stringArray $myStringArray (using powershell)

if matches, traverse into it and find <myflow:eventBooster> with event="start" and replace script content with $startContent1, and find event="end" replace with $endContent1

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:myflow="http://myflow.google.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="POC">
   <thread id="JALSA_KUSHI_ONLY" name="JALSA ONLY" isFine="true" myflow:isOk="true">
      <docprocess>My Doc Process</docprocess>
      <extensionElements>
         <myflow:historyLevel><![CDATA[audit]]></myflow:historyLevel>
      </extensionElements>
      <controlTask id="ReadDoc1" name="Read Doc" myflow:type="http">
         <extensionElements>
            <myflow:field name="rnhAsJson">
               <myflow:string><![CDATA[true]]></myflow:string>
            </myflow:field>
            <myflow:eventBooster event="start" >
               <myflow:scriptInfo scriptType="javascript" script="&quot;use strict&quot;;var loaded={require:require,exports:{}};" />
            </myflow:eventBooster>
            <myflow:eventBooster event="end" >
               <myflow:scriptInfo scriptType="javascript" script="&quot;use strict&quot;;var loaded={require:require,exports:{}},pending={};function require(e,t){define(void 0,e,t)};" />
               <myflow:field name="language">
                  <myflow:string><![CDATA[javascript]]></myflow:string>
               </myflow:field>
            </myflow:eventBooster>
         </extensionElements>
      </controlTask>
      <JimmyGate id="MyWish" name="My own" />
      <controlTask id="WriteDoc1" name="write task" myflow:type="http">
         <extensionElements>
            <myflow:field name="requestMethod">
               <myflow:string><![CDATA[POST]]></myflow:string>
            </myflow:field>
            <myflow:eventBooster event="start" >
               <myflow:scriptInfo scriptType="javascript" script="&quot;use strict&quot;;" />
            </myflow:eventBooster>
            <myflow:eventBooster event="end" >
               <myflow:scriptInfo scriptType="javascript" script="&quot;use strict&quot;;var loaded={require:require}" />
            </myflow:eventBooster>
         </extensionElements>
      </controlTask>
      <JimmyGate id="sid-iquweiq-1uy1-8173eu-817e81ye" />
   </thread>
</definitions>

I have controltaskId, but unable to traverse into it. But getting all the data matching with <controlTask>, <myflow:eventBooster>

$xmldata =( Select-Xml -Path $location\xyz.xml -XPath / ).Node;

$controltaskId = $xmldata.definitions.thread.controlTask.GetAttribute("id");

can someone please help here

CodePudding user response:

Here is a possible solution:

$xmldata.definitions.thread.controlTask.Where{ $_.id -in $myStringArray }.ForEach{
   $_.extensionElements.eventBooster.Where{ $_.event -eq 'start' }.ForEach{
       $_.scriptInfo.script = $startContent1
   }
   $_.extensionElements.eventBooster.Where{ $_.event -eq 'end' }.ForEach{
       $_.scriptInfo.script = $endContent1
   }
}
  • $xmldata.definitions.thread.controlTask gives us an array of all controlTask elements. Similarly, $_.extensionElements.eventBooster gives us an array of all eventBooster elements from the current extensionElements. See member access enumeration for how this works.
  • Use PowerShell intrinsic method .Where{} for filtering of arrays (alternatively use the Where-Object command).
    • Applied to .controlTask, filter by id attribute that is contained in $myStringArray.
    • Applied to .eventBooster, filter for matching event attribute.
  • PowerShell's intrinsic method .ForEach{} is used to iterate over the filtered element (alternatively use the ForEach-Object command). The syntax of the intrinsic methods is more concise and they perform better than the command alternatives so I prefer them for processing data structures (and not output of other commands).

CodePudding user response:

Try following :

using assembly System 
using assembly System.Linq
using assembly System.Xml.Linq 

$inputFilename = "c:\temp\test.xml"
$outputFilename = "c:\temp\test1.xml"
$xDoc = [System.Xml.Linq.XDocument]::Load($inputFilename)
$root = [System.Xml.Linq.XElement]$xDoc.Root
#Write-Host "root = " $root
$ns = [System.Xml.Linq.XNamespace]$root.GetNamespaceOfPrefix("myflow")
$eventBoosters = $xDoc.Descendants($ns   "eventBooster")
$starts = [System.Linq.Enumerable]::Where($eventBoosters, [Func[object,bool]]{ param($x) [string]$x.Attribute("event").Value -eq "start"})
foreach($start in $starts)
{
   [System.Xml.Linq.XElement]$start.SetAttributeValue("event", '$startContent1')
}
$ends = [System.Linq.Enumerable]::Where($eventBoosters, [Func[object,bool]]{ param($x) [string]$x.Attribute("event").Value -eq "end"})
foreach($end in $ends)
{
   [System.Xml.Linq.XElement]$end.SetAttributeValue("event", '$endContent1')
}
$xDoc.Save($outputFilename)
  • Related