xmlperlxml-libxml

Perl XML::LibXML update content


I am trying to update the content of an xml element. I am using XML::LibXML and need to use this library. Here is some sample data.

<data-table>
   <data>
      <number>1</number>
      <letter>one</letter>
   </data>
   <data>
      <number>2</number>
      <letter>two</letter>
   </data>
</data-table>

What I would like to do is change the contents of letter to "Purple" if the number is 2. However I believe I'm having a problem designating the Xpath for it to work. I get the following error

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

I've been struggling with this for days so any help is greatly appreciated.

 use XML::LibXML;
    my $p = XML::LibXML->new;
    my $d = $p->parse_fh(\*DATA);
    for my $node ($d->findnodes('//data-table/data')) 
    { 
       for my $childNode ($node->findnodes('./number/text()'))
       {
          if($childNode->textContent() == '1')
          { # here is where the problem is!
             my $changeNumerNode = $node->findnodes('./letter/text()'); 
             $changeNumerNode->setData("Purple");
          }
       }
    }
    print $d->toString;

   __DATA__
   <data-table>
      <data>
         <number>1</number>
         <letter>one</letter>
      </data>
      <data>
         <number>2</number>
         <letter>two</letter>
      </data>
  </data-table>

Solution

  • findnodes doesn't return a node. It returns a list of nodes or a ::NodeList. You could force it to return a list and take the first node returned.

    for my $node ($d->findnodes('//data-table/data')) 
    { 
       my ($number_node) = $node->findnodes('number')
          or next;
       $number_node->textContent() eq '1'
          or next;
       my ($letter_text) = $node->findnodes('letter/text()')
          or next;
       $letter_text->setData('Purple');
    }
    

    (As you can see, I removed the pure noise usage of ./.)

    You could even use

    for my $letter_text ($d->findnodes(
       '//data-table/data[number/text()="1"]/letter/text()')) 
    { 
       $letter_text->setData('Purple');
    }