0

Original XML:

<library>
<email name="Peter P">Peter_Parker@library.com</email>
</library>

Expected Result:

<library>
<email name="Peter Parker">Peter.Parker@library.com</email>
<address>London</address>
</library>

Further XML challenge:

<library>
<email name="Peter Parker">Peter.Parker@library.com</email>
<address> 
    <housenumber>1</housenumber>
    <street>Regent Street</street>
    <postcode>AB12YZ</postcode>
</address>
</library>

Code:

use strict;
use XML::LibXML;
use XML::LibXML::NodeList;

my $parser = XML::LibXML->new;
my $doc = $parser->parse_file("StackTest.xml");
my $root = $doc->getDocumentElement();

#to modify email address
my $email = $doc->findnodes("/library/email");
my $text = XML::LibXML::Text->new('Peter.Parker@library.com');
$email->replaceNode($text);

#to modify email name attribute
my $email_attribute = $doc->findnodes("/library/email");
my $email_name_att->setAttribute(q|name|,"Peter Parker");
$email_attribute->getAttribute(q|name|);

#to add <address tag> with value
my $address = $doc->createElement('address');
$address->appendText('London');
$root->appendChild($address);

print($doc->toString);

Error message:

Can't locate object method "replaceNode" via package "XML::LibXML::NodeList"

I'm a beginner and new to Perl script. I'd like to modify the XML file using Perl and XML::LibXML module. I also visited CPAN but it's very difficult to grasp the concept with very little related examples. If you could provide me some hints that would be great to improve my knowledge.

Happy to get any sort of feedback and willing to learn :)

NewUser
  • 3
  • 4
  • 2
    What have you tried? What problems are you having? Please show us your code. If you don't have any code, Stack Overflow is the wrong place to ask this question. – Dave Cross Aug 24 '17 at 10:11
  • How do you know Peter Parker lives in London? – simbabque Aug 24 '17 at 11:36
  • Apparently, Mr Parker is working as undercover photographer and I'm silly enough to give this information out. – NewUser Aug 24 '17 at 12:07

2 Answers2

3

From the documentation of XML::LibXML::Node:

findnodes evaluates the xpath expression (XPath 1.0) on the current node and returns the resulting node set as an array. In scalar context, returns an XML::LibXML::NodeList object.

Your calls are in scalar context because of the $variable = $doc->findnodes(...). You have three options:

  1. Use my $el = $doc->find(...) to return a single node.
  2. Alternatively, use the syntax my ($el) = $doc->findnodes(...). This results in a call in list context and assigns the first element of the returned list to $el.
  3. If there can be more nodes for your XPath expression, you can use a for expression to loop over the result of ->findnodes(...) like so:

    for my $el ($doc->findnodes(...) {
        print $el->tostring()
    }
    
Botje
  • 26,269
  • 3
  • 31
  • 41
0

Following code finally works for me, however, please comment if you notice anything I can improve.

use strict;
use XML::LibXML;
use XML::LibXML::NodeList;
use XML::LibXML::PrettyPrint;

my $parser = XML::LibXML->new;
my $doc = $parser->parse_file("StackTest.xml");
my $root = $doc->getDocumentElement();

#to modify email address
for my $email ($doc->findnodes('//library/email/text()')) {
    my $text = $email->getValue;
    $text =~ s{_}(\.);   
    $email->setData($text);
}

#to modify email name attribute
for my $email_attribute ($doc->findnodes('//library/email/@name')) {
    my $email_name_att = $email_attribute->getValue;
    $email_name_att =~ s{\sP}( Parker);
    $email_attribute->setValue($email_name_att);
}

#to add <address tag> with value
for my $addresstag ($doc->findnodes('//library')) {
    my $address = $doc->createElement('address');
    my $street = $doc->createElement('street');
    my $city = $doc->createElement('city');
    $addresstag->addChild($address);
    $address -> addChild($street);
    $address -> addChild($city);
    $street -> appendText('Forest Hills');
    $city -> appendText('New York');
}

#print($doc->toString);
print XML::LibXML::PrettyPrint
    -> new ( element => { compact => [qw/street/]})
    -> pretty_print($doc)
    -> toString;

Input XML file:

<library>
  <email name="Peter P">Peter_Parker@library.com</email>
</library>

Output XML file (manually formatted text here to make it clear):

<?xml version="1.0"?>
<library>
  <email name="Peter Parker">Peter.Parker@library.com</email>
  <address>
     <street> Forest Hills </street>
     <city> New York </city>
   </address>
</library>
NewUser
  • 3
  • 4