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

I am using Perl and XML::DOM to parse an XML file. In the XML file, there are tags that don't appear in all of the entries. How do I check to see if they are there before I try to read in the value. I have tried a bunch of things and they don't seem to work. I always get a value of "Can't call method "getFirstChild" on an undefined value at...". I have posted a sample code with the associated xml file.

PERL FILE:

#!/usr/bin/perl -w # File displayPipeTracer2.pl use XML::DOM; use Getopt::Long; my $testXML = qw/textXML.xml/; my $test4 = ""; my $parser = new XML::DOM::Parser; my $doc = $parser->parsefile ($testXML); foreach my $entry ($doc->getElementsByTagName('Entry')) { my $test1 = $entry->getElementsByTagName('test1')->item(0)->getFir +stChild->getNodeValue; my $test2 = $entry->getElementsByTagName('test2')->item(0)->getFir +stChild->getNodeValue; my $test3 = $entry->getElementsByTagName('test3')->item(0)->getFir +stChild->getNodeValue; #if (defined $entry->getElementsByTagName('test 4')->item(0)->getF +irstChild->getNodeValue) { # $test4 = $entry->getElementsByTagName('test4')->item(0)->getF +irstChild->getNodeValue; #} print "$test1, $test2, $test3"; print ", $test4" if defined $test4; print "\n"; }
XML FILE:
#!/usr/bin/perl -w # File displayPipeTracer2.pl use XML::DOM; use Getopt::Long; my $testXML = qw/textXML.xml/; my $test4 = ""; my $parser = new XML::DOM::Parser; my $doc = $parser->parsefile ($testXML); foreach my $entry ($doc->getElementsByTagName('Entry')) { my $test1 = $entry->getElementsByTagName('test1')->item(0)->getFir +stChild->getNodeValue; my $test2 = $entry->getElementsByTagName('test2')->item(0)->getFir +stChild->getNodeValue; my $test3 = $entry->getElementsByTagName('test3')->item(0)->getFir +stChild->getNodeValue; #if (defined $entry->getElementsByTagName('test 4')->item(0)->getF +irstChild->getNodeValue) { # $test4 = $entry->getElementsByTagName('test4')->item(0)->getF +irstChild->getNodeValue; #} print "$test1, $test2, $test3"; print ", $test4" if defined $test4; print "\n"; }

Replies are listed 'Best First'.
Re: Parsing an XML file with an undefined value
by Fletch (Bishop) on Nov 12, 2008 at 18:49 UTC

    Your defined test doesn't work because it's $entry->getElementsByTagName('test4')->item(0) that's returning undef and you're then trying to call getFirstChild on that You need to check that this value is actually present before using it as an object.

    Another alternative might be to look at something which supports XPath (e.g. XML::Twig) and come up with an XPath expression which will return just the elements of interest (if they're not there, you'll get nothing back rather than having to test for defined-ness of return values at multiple levels).

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      I tried your first suggestion and I was not successful. Here is what I tried:
      if (defined $entry->getElementsByTagName('test 4')) { print "reached 1\n"; if (defined $entry->getElementsByTagName('test 4')->item(0)) { print "reached 2\n"; if (defined $entry->getElementsByTagName('test 4')->item(0)->g +etFirstChild ) { print"reached 3\n"; if (defined $entry->getElementsByTagName('test 4')->item(0 +)->getFirstChild->getNodeValue ) { print"reached 4\n"; $test4 = $entry->getElementsByTagName('test4')->item(0 +)->getFirstChild->getNodeValue; } } } }
      It only gets past the first if statement. The if (defined $entry->getElementsByTagName('test 4')->item(0)) never passes. Am I doing something wrong?
Re: Parsing an XML file with an undefined value
by Herkum (Parson) on Nov 12, 2008 at 19:01 UTC
    You could try something like this,
    for my $entry ($doc->getElementsByTagName('Entry')) { FIELD: for my $field (qw(test1 test2 test3 test4 test5)) { my $test = $entry->getElementsByTagName( $field )->item(0); if (not $test) { warn "No item found for tag $field\n"; next FIELD; } warn "Node Value for $field => " . $text->getFirstChild->getNo +deValue . "\n"; } }
      Thanks to both of you. I figured it out based on your comments. This is what I did:
      my $text = qw/test4/; $test4 = $entry->getElementsByTagName( $text)->item(0); if (defined $test4) { $test4 = $entry->getElementsByTagName( $text)->item(0)->getFirstCh +ild->getNodeValue; }
      Reps to both of you!
        I am a novice to perl and trying to implement this approach in my code. I am using ActivePerl on Windows. 2 questions: 1. I am getting an error stating that "parsefile" is undefined. I have defined the following modules in my code:
        use strict; use XML::Twig; use File::Basename; use XML::DOM;
        since I use parsefile through Twig for my previous code, would this be the cause of my error? I think it's interfering with calling parsefile through DOM. 2. Would this approach also work if I want to validate attributes instead of tags in an XML file? Thanks