Search code examples
xmlperlnodesxml-libxml

Perl XML::LibXML, findnodes can only read the root of the XML file


I'm trying to parse this .kml file :

<?xml version="1.0" encoding="utf-8" ?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document id="root_doc">
<Schema name="PostalCodeCanada" id="PostalCodeCanada">
    <SimpleField name="ZIP" type="string"></SimpleField>
    <SimpleField name="VERTICES" type="int"></SimpleField>
</Schema>
<Folder><name>PostalCodeCanada</name>
  <Placemark>
    <Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
    <ExtendedData><SchemaData schemaUrl="#PostalCodeCanada">
        <SimpleData name="ZIP">G1Y1B1</SimpleData>
        <SimpleData name="VERTICES">5</SimpleData>
    </SchemaData></ExtendedData>
      <Polygon><altitudeMode>relativeToGround</altitudeMode><outerBoundaryIs><LinearRing><altitudeMode>relativeToGround</altitudeMode><coordinates>-73.604399,45.545611 -73.603988,45.545886 -73.602861,45.547715 -73.602861,45.547715 -73.604399,45.545611 -73.604399,45.545611</coordinates></LinearRing></outerBoundaryIs></Polygon>
  </Placemark>
  <Placemark>
    <Style><LineStyle><color>ff0000ff</color></LineStyle><PolyStyle><fill>0</fill></PolyStyle></Style>
    <ExtendedData><SchemaData schemaUrl="#PostalCodeCanada">
        <SimpleData name="ZIP">G1Y1B2</SimpleData>
        <SimpleData name="VERTICES">5</SimpleData>
    </SchemaData></ExtendedData>
      <Polygon><altitudeMode>relativeToGround</altitudeMode><outerBoundaryIs><LinearRing><altitudeMode>relativeToGround</altitudeMode><coordinates>-73.604399,45.545611 -73.603988,45.545886 -73.602861,45.547715 -73.602861,45.547715 -73.604399,45.545611 -73.604399,45.545611</coordinates></LinearRing></outerBoundaryIs></Polygon>
  </Placemark>
</Folder>
</Document></kml>

I'm using Perl with XML::LibXML, but findnodes can't read any node except '/'. Here's my code:

#!/usr/bin/env perl

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

my $outputFilename = "PostalCodesCollegePro.kml";

my $intro = '<?xml version="1.0" encoding="utf-8" ?>'."\n".'<kml xmlns="http://www.opengis.net/kml/2.2">'."\n".'<Document id="root_doc">'."\n".'<Schema name="PostalCodeCanada" id="PostalCodeCanada">'."\n\t".'<SimpleField name="ZIP" type="string"></SimpleField>'."\n\t".'<SimpleField name="VERTICES" type="int"></SimpleField>'."\n".'</Schema>'."\n".'<Folder><name>PostalCodeCanada</name>'."\n";
my $outro = '</Folder>'."\n".'</Document></kml>'."\n";

open (my $fh, ">".$outputFilename) or die "Impossible d'ouvrir le fichier d'écriture";
print $fh $intro;

my $xml = XML::LibXML->new();
my $data = $xml->parse_file("PostalCodeCanada.kml");
foreach my $node ( $data->findnodes('//Folder') ) {
    print ($node->toString);
#   my($zip) = $node->findnodes('./ExtendedData/SchemaData/SimpleData');
#   print ($zip->to_literal."\n");
#   if ($zip->to_literal =~ /(^G1Y)|(^G3A)|(^G2G)|(^G3L)|(^G3H)|G0A2R0|G0A1T0|G0A1L0|G0A3H0|G0A3G0|G0A2Y0|G0A2Z0|G0A4N0|G0A2J0|G0A3M0|G0A4A0|G0A1A0|G0A1Y0|G0A1S0|G0A4B0|G0A3T0|G0A3B0|G0A4H0|G0A1W0|G0A3L0|G0A4L0|G0A3A0/){
#       print $fh $node->to_literal;
#   }
}

print $fh $outro; 
close $fh or warn "Impossible de fermer le fichier après écriture";`

Thanks to everybody that'll give some help! PS: This is a downsized .kml file, in fact the real one has all geographic information of all Canadian postal codes. I'm tring to generate another .kml containing only the desired postal code in order to generate a map with the Google Map API.


Solution

  • Your problem is that your nodes are all within a namespace, so you need to deal with that. The easiest way is probably to use an XML::LibXML::XPathContext object.

    my $xml = XML::LibXML->new();
    my $data = $xml->parse_file("PostalCodeCanada.kml");
    
    my $xpc = XML::LibXML::XPathContext->new($data);
    $xpc->registerNs('k', 'http://www.opengis.net/kml/2.2');
    
    foreach my $node ( $xpc->findnodes('//k:Folder') ) {
      ...
    }