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

So, it turns out inserting a new node into an existing xml list is not as easy as I thought. I'm sure I'm missing something but been tinkering with this for quite some time now so I ask you for some guidance.

In short, I'm using a file as a base model which is being read in that looks similar to this.

<top> <nodes> <node> <label>Office</label> <node> <label>1st Floor</label> </node> <node> <label>2nd Floor</label> </node> <node> <nodes> </top>
Say I want to add something to the 1st floor like this. <node> <label>10.1.1.1</label> </node> I've gone through many different variations so I'll post one of them as the closest I can come is appending it to the end and not to the floor I want.
#!/usr/bin/perl use XML::LibXML; $filename = "Office.xml"; my $floor = '1st Floor'; my $bldg = "Building A"; my $parser = XML::LibXML->new(); my $doc = $parser->parse_file("$filename") or die; my $root = $doc->getDocumentElement(); my $parent = $doc->documentElement; my $newnode = $doc->documentElement; my $query = "//node[label = '$bldg']/node[label = '$floor']/label/tex +t()"; if(my($node) = $doc->findnodes($query)) { my $new_element= $doc->createElement("IP"); $new_element->appendText('10.1.1.1'); $newnode = $parent->addSibling($new_element); print $newnode -> toString; }
Can someone please help?

Replies are listed 'Best First'.
Re: libxml - insert node
by tobyink (Canon) on Feb 14, 2012 at 09:46 UTC
    #!/usr/bin/perl use strict; use XML::LibXML 1.70; # Parse the document. XML::LibXML 1.70 introduced the load_xml method, # which is somewhat nicer than the pre-1.70 methods for parsing data. my $doc = XML::LibXML->load_xml(IO => \*DATA) or die; # In your original example, the building was "Building A", but there # was no node called "Building A" in the sample XML. my $bldg = 'Office'; my $floor = '1st Floor'; # I've changed your query so that it selects a <node> element. # Previously it drilled down further to select the text inside # the node's <label> element. There didn't seem to be any reason # to do that. my $query = "//node[label = '$bldg']/node[label = '$floor']"; # Here's one way to do it... if( my ($node) = $doc->findnodes($query) ) { my $new_node = $doc->createElement("node"); my $new_label = $doc->createElement("label"); $node->addChild($new_node); $new_node->addChild($new_label); $new_label->appendText('10.1.1.1'); } # This way is a little more concise... if( my ($node) = $doc->findnodes($query) ) { $node -> addNewChild(undef, 'node') -> addNewChild(undef, 'label') -> appendText('10.2.2.2'); } # This outputs the XML nicely indented. If you don't care # about indentation, just use print $doc->toString. use XML::LibXML::PrettyPrint; print XML::LibXML::PrettyPrint -> new ( element => { compact => [qw/label/] } ) -> pretty_print($doc) -> toString; __DATA__ <top> <nodes> <node> <label>Office</label> <node> <label>1st Floor</label> </node> <node> <label>2nd Floor</label> </node> </node> </nodes> </top>
      Thanks Tobyink! Although your updated method of node inserting worked, your more "precise" method returned some errors and will post them later. Never the less, one worked, that's all that matters! :)
Re: libxml - insert node
by choroba (Cardinal) on Feb 14, 2012 at 11:24 UTC
    I usually use XML::XSH2 for XML manipulation. In this case, it can be used as follows:
    open Office.xml ; my $new := insert element IP into //node[label="Office"]/node[label="1 +st Floor"] ; set $new/text() '10.1.1.1' ; save :b ;
Re: libxml - insert node
by ikegami (Patriarch) on Feb 14, 2012 at 18:28 UTC
    "//node[label = '$bldg']/node[label = '$floor']/label/text()"
    should be
    "//node[label = '$bldg']/node[label = '$floor']/label"
    and
    $newnode = $parent->addSibling($new_element);
    should be
    $node->addSibling($new_element);