Reputation: 5502
I have an xml file as given below
<Books>
<Book rev="19ver" nver="1.0.0.0" >
<Book rev="19Sub4" nver="1.4.0.250">
<Book rev="19Sub5" nver="1.5.0.250" >
<Book rev="19Sub5Fix2" nver="1.5.2.250" req="true">
</Book>
<Book rev="19Sub5Fix1" nver="1.5.1.250" req="false">
</Book>
</Book>
</Book>
<Book rev="20ver" nver="2.0.0.0" >
<Book rev="20Sub3" nver="2.3.0.1111">
<Book rev="20Sub3Fix5" nver="2.3.5.1111" req="true" >
</Book>
<Book rev="20Sub3Fix4" nver="2.3.4.1111" req="true" >
</Book>
<Book rev="20Sub3Fix3" nver="2.3.3.1111" req="false" >
</Book>
<Book rev="20Sub3Fix2" nver="2.3.2.1111" req="false" >
</Book>
<Book rev="20Sub4" nver="2.4.0.1567" >
<Book rev="20Sub4Fix5" nver="2.4.5.1567" req="true" >
</Book>
<Book rev="20Sub4Fix4" nver="2.4.4.1567" req="true" >
</Book>
<Book rev="20Sub4Fix3" nver="2.4.3.1567" req="false" >
</Book>
<Book rev="20Sub4Fix2" nver="2.4.2.1567" req="false" >
</Book>
<Book rev="20Sub4Fix1" nver="2.4.1.1567" req="false" >
</Book>
</Book>
</Book>
</Book>
</Book>
</Books>
I have a powershell script to add a new element inside the specified node. For example if i want to enter the element with rev "20Sub3Fix6" and nver "2.3.6.1111" inside "20Sub3" i can use the following code.
$parentXML = Select-Xml -Xml $bookInfo -XPath "//*[@rev='20Sub3']"
$bookInfo = Get-Content -Raw -Path $XMLPATH
$book = $bookInfo.CreateNode("element","book","")
$book.SetAttribute("rev","20Sub3Fix6")
$book.SetAttribute("nver","2.3.6.1111")
$book.SetAttribute("req","true")
$rslt = $parentXML.Node.prependChild($book)
$bookInfo.Save($XMLPATH)
What I'm looking is to group the element based on the value of attribute "req". When I add an element with req "false" then it should add on top of the last element with "req=false. Also when I add an element with req "true" then it should add on top of the last element with "req=true".
<Book rev="20Sub1" nver="2.1.0.769" >
<Book rev="20Sub1Fix7" nver="2.1.7.769" req="true" >
</Book>
<Book rev="20Sub1Fix6" nver="2.1.6.769" req="true" >
</Book>
<Book rev="20Sub1Fix4" nver="2.1.4.769" req="true" >
</Book>
<Book rev="20Sub1Fix3" nver="2.1.3.769" req="false" >
</Book>
<Book rev="20Sub1Fix2" nver="2.1.2.769" req="false" >
</Book>
<Book rev="20Sub1Fix1" nver="2.1.1.769" req="false" >
</Book>
</Book>
Adding C# and .Net tags also because if you know the answer in C# then you can post that too
Upvotes: 0
Views: 219
Reputation: 61148
I have formatted your XML to have a better understanding of its format:
<Books>
<Book rev="19ver" nver="1.0.0.0" >
<Book rev="19Sub4" nver="1.4.0.250">
<Book rev="19Sub5" nver="1.5.0.250" >
<Book rev="19Sub5Fix2" nver="1.5.2.250" req="true"></Book>
<Book rev="19Sub5Fix1" nver="1.5.1.250" req="false"></Book>
</Book>
</Book>
<Book rev="20ver" nver="2.0.0.0" >
<Book rev="20Sub3" nver="2.3.0.1111">
<Book rev="20Sub3Fix5" nver="2.3.5.1111" req="true" ></Book>
<Book rev="20Sub3Fix4" nver="2.3.4.1111" req="true" ></Book>
<Book rev="20Sub3Fix3" nver="2.3.3.1111" req="false" ></Book>
<Book rev="20Sub3Fix2" nver="2.3.2.1111" req="false" ></Book>
<Book rev="20Sub4" nver="2.4.0.1567" >
<Book rev="20Sub4Fix5" nver="2.4.5.1567" req="true" ></Book>
<Book rev="20Sub4Fix4" nver="2.4.4.1567" req="true" ></Book>
<Book rev="20Sub4Fix3" nver="2.4.3.1567" req="false" ></Book>
<Book rev="20Sub4Fix2" nver="2.4.2.1567" req="false" ></Book>
<Book rev="20Sub4Fix1" nver="2.4.1.1567" req="false" ></Book>
</Book>
</Book>
</Book>
</Book>
</Books>
To do what you want, you could use the code below:
# load the xml from file
$xml= New-Object System.XML.XMLDocument
$xml.Load("D:\Test\MyBooks.xml")
# create your new node
$newNode = $xml.CreateElement("Book")
$newNode.SetAttribute("rev", "20Sub3Fix6")
$newNode.SetAttribute("nver", "2.3.6.1111")
$newNode.SetAttribute("req", "true")
# get the parentnode to insert the new node in
$node20Sub3 = $xml.SelectSingleNode("//Book[@rev='20Sub3']")
# find the first node where attribute 'req' is 'true'
$insertBefore = ($node20Sub3.ChildNodes | Where-Object { $_.rev.StartsWith("20Sub3Fix") -and $_.req -eq 'true' })[0]
# insert your new node on top of that
$node20Sub3.InsertBefore($newNode, $insertBefore)
# save the XML
$xml.Save("D:\Test\MyBooks.xml")
Result:
<Books>
<Book rev="19ver" nver="1.0.0.0">
<Book rev="19Sub4" nver="1.4.0.250">
<Book rev="19Sub5" nver="1.5.0.250">
<Book rev="19Sub5Fix2" nver="1.5.2.250" req="true"></Book>
<Book rev="19Sub5Fix1" nver="1.5.1.250" req="false"></Book>
</Book>
</Book>
<Book rev="20ver" nver="2.0.0.0">
<Book rev="20Sub3" nver="2.3.0.1111">
<Book rev="20Sub3Fix6" nver="2.3.6.1111" req="true"></Book>
<Book rev="20Sub3Fix5" nver="2.3.5.1111" req="true"></Book>
<Book rev="20Sub3Fix4" nver="2.3.4.1111" req="true"></Book>
<Book rev="20Sub3Fix3" nver="2.3.3.1111" req="false"></Book>
<Book rev="20Sub3Fix2" nver="2.3.2.1111" req="false"></Book>
<Book rev="20Sub4" nver="2.4.0.1567">
<Book rev="20Sub4Fix5" nver="2.4.5.1567" req="true"></Book>
<Book rev="20Sub4Fix4" nver="2.4.4.1567" req="true"></Book>
<Book rev="20Sub4Fix3" nver="2.4.3.1567" req="false"></Book>
<Book rev="20Sub4Fix2" nver="2.4.2.1567" req="false"></Book>
<Book rev="20Sub4Fix1" nver="2.4.1.1567" req="false"></Book>
</Book>
</Book>
</Book>
</Book>
</Books>
Upvotes: 1
Reputation: 167706
Try e.g. $rslt = $parentXML.Node.InsertBefore($book, $parentXML.Node.SelectSingleNode(string.Format("Book[@req = '{0}']", $req)))
. So basically introduce a variable for the req value, select the right sibling using SelectSingleNode
if it exists and use InsertBefore
.
I am not sure Powershell groks the C# string.Format
but I guess you can adapt that (System.String.Format
?).
Upvotes: 0