0

I'm currently working on Perl script that will mock node values based on specific criteria. I have similar parents nodes with in a file, out of which on specific criteria I should replace/edit child node value. Given Sample file below. I'm using XML::LibXML module to do this work for me.

But it seems to be not working as expected, because my below piece of code is always replacing node values from the first found parent node irrespective of the criteria that I'm providing.

Here is my Piece of code that I have tested.

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

##ERROR KEY ENTRY_MISSING 

if ( my $valdExpHBill = $doc -> findnodes('//shipment//Flag[string()="true"]')) {
        print "valid flag is true\n";

     ##ID_MISSING 
     if (my $buEnShp = $doc -> findnodes ('//shipment//business//Type[string()="Shipper"]')) {
            print "Shipper business exist under this shipment\n";
            my ($buEnShpGciVal) = $doc->findnodes('//shipment//business//number//text()');  
            $buEnShpGciVal->setData(' ');
            my ($buEnShpBNoVal) = $doc->findnodes('//shipment//business//busNumber//text()');   
            $buEnShpBNoVal->setData(' ');
            my $EDE5 = $doc->toFile("ID_MISSING.xml",1);
    }
}

Sample Input File (Test.xml)::

<shipment>
  <flag>true</flag>
  <business>
    <Type>Consignee</Type>
    <number>G0213</number>
    <busNumber>3006</busNumber>
  </business>
  <business>
    <Type>Shipper</Type>
    <number>G0113</number>
    <busNumber>3116</busNumber>
  </business>
</shipment>

Out Put file (ID_MISSING.xml)::

<shipment>
  <flag>true</flag>
  <business>
    <Type>Consignee</Type>
    <number> </number>
    <busNumber> </busNumber>
  </business>
  <business>
    <Type>Shipper</Type>
    <number>G0113</number>
    <busNumber>3116</busNumber>
  </business>
</shipment>

Expected output (ID_MISSING.xml)::

<shipment>
  <flag>true</flag>
  <business>
    <Type>Consignee</Type>
    <number>G0213</number>
    <busNumber>3006</busNumber>
  </business>
  <business>
    <Type>Shipper</Type>
    <number> </number>
    <busNumber> </busNumber>
  </business>
</shipment>

Actually I want to match Shipper parent node and want to replace its child elements with blanks. But always it is matching the first found parent node and replacing/editing those values. I want to achieve this using XML::LibXML module, even though I can other options using some other XML related modules in Perl.

As per my understanding, even though I'm checking for specific condition, it is just parsing the XML document and getting first found parent node and replacing it's child node values. I just want to understand how exactly I can edit child nodes of specific parent node. Your help is appreciated in this.

1
  • Okay I tried using for loop checking for exact node by giving following condition for my $buEnCng ($doc5 -> findnodes('//shipment//businessEntity[businessType/text()="Consignee"]//gciNumber//text()')) based on which I'm replacing data using $buEnCng->setData('0000'); and then writing it to file using my $EDE6 = $doc5->toFile("CONSIGNEE_ID_MISSING.xml",1);. It is working perfectly. Commented May 22, 2018 at 9:30

1 Answer 1

2

If you want to only search in a subtree of a node, call findnodes as a method of the node, not of the whole document object.

#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };

use strict;
use warnings;
use XML::LibXML;

my $doc = 'XML::LibXML'->load_xml(location => shift);
my $root = $doc->getDocumentElement;

if ( my $valdExpHBill = $doc -> findnodes('//shipment[flag="true"]')) {
    print "valid flag is true\n";

    if (my ($buEnShp) = $doc->findnodes('//shipment//business[Type="Shipper"]')) {
        print "Shipper business exist under this shipment\n";
        my ($buEnShpGciVal) = $buEnShp->findnodes('number/text()');
        $buEnShpGciVal->setData(' ');
        my ($buEnShpBNoVal) = $buEnShp->findnodes('busNumber/text()');
        $buEnShpBNoVal->setData(' ');
        my $EDE5 = $doc->toFile("ID_MISSING.xml",1);
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks a lot for your response and solution. I have tried this and could see following error at the line my ($buEnShpGciVal) = $buEnShp->findnodes('number/text()'); Error message is Can't locate object method "findnodes" via package "XML::LibXML::NodeList I understood that findnodes should be called as a methods for that node instead of whole document but ending up with the above mentioned error.
And I have tried printing $buEnShp to check what exactly it is holding and could see that it is just holding the values of the complete parent node with out any tags. It is just holding values of all the elements in the matching node. Is this error something related to this data where only values are being stored?
Could you please clarify on why exactly the above mentioned error is coming? I suspect that since $buEnShp is holding values alone with out any nodes or tags, findnodes is throwing error. I'm trying to figure it out by trying in otherways meanhwhile, but no luck.
findnodes returns a node list every time, even when the node list has just one element. You can't call findnodes on a node list, you have to call it on a node. That's why I used my ($...) = ...->findnodes: the parentheses on the left hand side force list context that means only the first node gets assigned.
I understood why exactly you used findnodes on node instead of nodelist, but my real doubt is why I'm getting error on my ($buEnShpGciVal) = $buEnShp->findnodes('number/text()'); this line. As I mentioned in my first comment, it is throwing Can't locate object method "findnodes" via package "XML::LibXML::NodeList
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.