http://qs321.pair.com?node_id=1082058

anadem has asked for the wisdom of the Perl Monks concerning the following question:

I need to change some xml data, and I'm stuck trying to identify the node to modify, so hoping someone can dispel my ignorance.

This is a sample of the xml data file ('n4000-small.xml'); the real data has many matrix elements, not just two, and has siblings of the vm elements:
<formation name="stoneridge" version="1.6"> <block> <matrix> <vm type="sns"> <release version="8.5.e"> <supported> <fixed>123.1106</fixed> </supported> </release> </vm> <vm type="br"> <release version="7.2.2"> </release> </vm> </matrix> <matrix> <vm type="sns"> <release version="4.1.e"> <supported> <min>124.1306</min> <max>124.1500</max> </supported> </release> </vm> <vm type="br"> <release version="7.2.1"> </release> </vm> </matrix> </block> </formation>
I need to locate the 'matrix' node which has a 'vm' node of 'type="br"' with a specific 'release version' attribute value, then change that matrix's 'vm type="sns"' values (more on that below). I can't figure out what's wrong with my code:
use strict; use warnings; use XML::LibXML; my $file = 'n4000-small.xml'; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file($file); # THIS WORKS - but it's finding the vm node not the matrix node foreach my $vm ($doc->findnodes('//vm[@type=\'br\']')) { print "br ", $vm->findvalue("./release/\@version"), "\n"; if ( '7.2.1' eq $vm->findvalue("./release/\@version")) { print "found 7.2.2\n" } } # this also works, finding the "br" vm with the right version, but I t +hink I need the matrix, to identify the "sns" vm my @wanted_vm = ($doc->findnodes('//vm[@type=\'br\' and ./release/@ve +rsion=\'7.2.1\']')); if( @wanted_vm ) { print "found br ", $wanted_vm[0]->findvalue('./release/@version'), + "\n"; } # this gets the 'br' matrices, but why doesn't it get the vm attribute +s? foreach my $matrix ($doc->findnodes( '//matrix/vm[@type=\'br\']' )) { if( $matrix ) { #print "matrix:", $matrix->toString(), "\n"; print "vm release version:", $matrix->findvalue('./vm/release/ +@version'), "\n"; my $vm_type = $matrix->findvalue('./vm/@type'); print "vm type:'" . $vm_type . "'" . "\n"; } }

Given that 'matrix' is parent of 'vm', can some kind, wiser monk please say why xpath './release/@version' of a vm node works, but './vm/release/@version' xpath from a matrix node does not? (I'm not certain this is the right terminology, but hope it's understandable.)

I haven't got as far as changing the "sns" vm's data yet. There will be two things to do: (i) change the value of the sns vm's 'release version' attribute; and (ii) either change the '//supported/fixed' element's text, or if there are '//supported/max' and '//supported/min' elements they must be removed and replaced by a '//supported/fixed' element with given text. Any gestures in those directions (especially on how to add the 'fixed' element) will also be very helpful and much appreciated.