1

If I have a file with XML like:

<?xml version="1.0" encoding="utf-8"?>
<package date-created="19/07/2016" version="6" system="6">
  <description />
  <system />
  <res id="1057002654" name="ABC" path="/DEF" type="F" mimeType="application/x-shared">
    <description />
    <keywords />
    <resver id="1057014163" major="1" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057014677" major="2" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057015106" major="3" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057016183" major="4" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057016374" major="5" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057017622" major="6" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057017704" major="7" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
    <resver id="1057017863" major="8" minor="0" revision="0" effective-from="20010101000000" effective-to="20991231235959" />
  </res>
</package>

There could be hundreds of items, and within them there could be one or more . In Powershell, I'm trying to look for the last resver id value where the res id equalls 1057002654.

I started off with this:

$path = 'C:\TEST\package.xml'
$myxml = [xml](Get-Content $path)
$node = $myxml.package.res.resver | where {$_.id -eq "1057002654"}
$node.id = '123'
$myxml.Save($path)

This was just a quick test to see if I could get to the bit I wanted and update it, but that fails with:

Property 'id' cannot be found on this object; make sure it exists and is settable.

I then tried something like:

$xml = [xml](Get-Content 'C:\TEST\package.xml')
$nodes = $xml.SelectNodes("//*[@res]")
foreach ($node in $nodes) {

$node.Attributes | %{$_}  > C:\TEST\disk.txt }

This actually writes all the values in the attributes to a .txt file but cannot seem to find a way to actually do what I want.

/Edit - Corrected with what I actually meant to do. Ended up with code like this, where $Ref is an item in the array $References

foreach ($Ref in $References)
{
    $xml = [xml](Get-Content 'C:\TEST\package.xml')
    $res = $xml.SelectSingleNode('//res[@id = $Ref]/child::resver')
    $resource = $res.id + ".txt"

    # more stuff here
} 

Getting this error though:

Exception calling "SelectSingleNode" with "1" argument(s): "Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function."
At C:\TEST\script.ps1:22 char:38
+ [string] $res = $xml.SelectSingleNode <<<< ('//res[@id = $Ref]/child::resver')
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException
hshah
  • 842
  • 4
  • 14
  • 35
  • 5
    You should [not use a regex](http://stackoverflow.com/a/1732454/52598) to parse xml but xpath or xquery. fwiw - I can recommend RegexBuddy to get a better feel for regex *(not affiliated but avid user)*. Composing the xquery is left as an excercise *(read as: not my cup of tea, sorry)* – Lieven Keersmaekers Jul 20 '16 at 05:31
  • 2
    That's not valid XML, the node name itself cannot have an attribute value (and `=` is not allowed as part of a name) – Mathias R. Jessen Jul 20 '16 at 07:41
  • @LievenKeersmaekers - apologies for the really rubbish question. I have completely refactored it so it would be greatly appreciated if you could take a look please. – hshah Jul 20 '16 at 17:35
  • @MathiasR.Jessen, you were right and I was being an idiot. Sorry! – hshah Jul 20 '16 at 17:36
  • 1
    `$xml.SelectNodes("//res[resver/@id=1057014163]/@id")` – user4003407 Jul 20 '16 at 17:42
  • @PetSerAl Thank you very very much!!! :) – hshah Jul 20 '16 at 20:39
  • 2
    @hshah - really no need to apoligize, I was not offended in any way :). You have your answer (I couldn't provide it even if I wanted to) and I learned from it. Thank you ;) – Lieven Keersmaekers Jul 25 '16 at 18:49

1 Answers1

3

Use the parent:: axis to find the appropriate parent node after locating the <resver /> node with the correct ID:

$path = 'C:\TEST\package.xml'
$myxml = [xml](Get-Content $path)
# Locate resver, select parent res node
$res = $myxml.SelectSingleNode('//resver[@id = "1057014163"]/parent::res')
# Update id
$res.SetAttribute('id','123')
# Save document
$myxml.Save($path)

You could also use the .. alias for the parent node:

//resver[@id = "1057014163"]/..

Or as PetSerAl notes, select the res node that has the described child node:

//res[resver/@id = "1057014163"]

For doing it the other way around (find all resver child node id values based on parent nodes id):

$ids = $myxml.SelectNodes('//res[@id = "1057002654"]/resver[@id]') |Select -Expand id

(notice use of SelectNodes() rather than SelectSingleNode())

Community
  • 1
  • 1
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Thanks to both you and @PetSerAl. Had been driving me nuts for ages! :) – hshah Jul 20 '16 at 20:40
  • Sorry guys, added a little more to my question as I realised I was being an idiot and looking at it the other way around. I have the res ids and need the last resver id. Got the code but it is giving me errors :( – hshah Jul 26 '16 at 15:03
  • I was still using SelectSingleNode() as I only want the last node :) – hshah Jul 26 '16 at 16:33
  • I think my error is occuring because I am trying to use a variable in the SelectSingleNode() and it doesn't seem to like that. – hshah Jul 26 '16 at 16:53
  • Swap double-quotes with single-quotes if you want variable expansion – Mathias R. Jessen Jul 26 '16 at 16:54
  • I swear I don't have any double quotes in this bit: $res = $xml.SelectSingleNode('//res[@id = $Ref]/child::resver') – hshah Jul 26 '16 at 16:56
  • $Ref is the variable from foreach ($Ref in $References) but I have a feeling that I am doing something silly here because write-host $Ref doesn't come back with anything. – hshah Jul 26 '16 at 16:57
  • *Replace* single-quotes with double-quotes then :-) – Mathias R. Jessen Jul 26 '16 at 17:00
  • Doesn't seem to work... tried a variety of combinations, even tried escaping quotes as well. Get errors like: Exception calling "SelectSingleNode" with "1" argument(s): "Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function." – hshah Aug 01 '16 at 04:07