1

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>
Cœur
  • 37,241
  • 25
  • 195
  • 267
ScottEdge
  • 87
  • 2
  • 9
  • `findnodes` doesn't return a node. It returns a list of nodes or a NodeList. See the answer to the linked question for how to do it. – ikegami Feb 09 '13 at 23:24
  • thanks. I saw that in my searches. but I can't delete the node either. I realize findnodes doesn't return a node but I am looping though the nodes once I get one why can't I perform actions on it then? That's the part I'm not understanding. Or the correct way to go about it in the first place. So would I have to delete all child nodes of when I found a match then recreate all the child nodes of ? that would not be efficient in my overal scenario. – ScottEdge Feb 09 '13 at 23:35
  • You seem to have missed that the linked post shows how to get a node – ikegami Feb 09 '13 at 23:38

1 Answers1

2

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');
}
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • I'm thankful for this answer but discouraged at how long I read through documentation and examples but didn't think to do it like your second example. – ScottEdge Feb 09 '13 at 23:41
  • you've taken 12 lines of code that I had and turned it into 6. – ScottEdge Feb 09 '13 at 23:43
  • XML::LibXML doesn't document XPaths. I really call that 2 or 3 lines. – ikegami Feb 09 '13 at 23:43
  • Yeah I could not find any documentation Xpaths. and it is really 2 line but I meant in my overall project. This was just a simple example I pulled out so I could get help. Thanks again I really appreciate it. – ScottEdge Feb 09 '13 at 23:53