2

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.

whippet
  • 25
  • 4

3 Answers3

2

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') ) {
  ...
}
Dave Cross
  • 68,119
  • 3
  • 51
  • 97
2

Your XML data uses a default namespace which must be specified explicitly when you are accessing it using XPath. Where XML::LibXML is concerned, that means you must create an XML::LibXML::XPathContext object to search for the data

Here's an example program that does what you need

#!/usr/bin/env perl

use strict;
use warnings;

use XML::LibXML;

my $doc = XML::LibXML->load_xml(location => 'PostalCodeCanada.kml');
my $xpc = XML::LibXML::XPathContext->new($doc);
$xpc->registerNs( gis => 'http://www.opengis.net/kml/2.2');

for my $folder ( $xpc->findnodes('/gis:kml/gis:Document/gis:Folder') ) {

    my ($zip) = $xpc->findnodes('gis:Placemark/gis:ExtendedData/gis:SchemaData/gis:SimpleData', $folder);
    $zip = $zip->to_literal;

    print "$zip\n";

    if ( $zip =~ /(?:G0A(?:1A0|1L0|1S0|1T0|1W0|1Y0|2J0|2R0|2Y0|2Z0|3A0|3B0|3G0|3H0|3L0|3M0|3T0|4A0|4B0|4H0|4L0|4N0)|G1Y|G1Y1B1|G2G|G3A|G3H|G3L)/){
        print $folder->to_literal;
    }
}
Borodin
  • 126,100
  • 9
  • 70
  • 144
0

You already have an answer from within XML::LibXML. However I'll point out - if you use XML::Twig you can ignore the namespace. (That's because it doesn't really support them - which is fine if you've only got one!)

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig -> new -> parsefile ( 'input.kml');
foreach my $node ( $twig -> findnodes ( '//Folder') ) {
   print $node -> text,"\n";
}
Sobrique
  • 52,974
  • 7
  • 60
  • 101