3

I want a method in perl to do the following operation sample xml file

<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/> First Country
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/> Second Country
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/> Third Country
    </country>
</data>

when i enter the elment rank as a input,then the output should be like below.

<?xml version="1.0"?>
    <data>
        <country name="Liechtenstein">
            <rank>1</rank>
        </country>
        <country name="Singapore">
            <rank>4</rank>
        </country>
        <country name="Panama">
            <rank>68</rank>
        </country>
    </data>
Venkatesan
  • 422
  • 3
  • 6
  • 19
  • You can do that transformation very easily using a XLST document. To transform using XSLT you can use [XML::LibXSLT](http://search.cpan.org/dist/XML-LibXSLT/). – helderdarocha May 19 '14 at 13:10

3 Answers3

3

This solution uses XML::LibXML, and works by finding the child of each country element that has the required node name, then removing all the children and adding back the selected element.

use strict;
use warnings;

use XML::LibXML;

my $doc = XML::LibXML->load_xml(IO => *DATA, no_blanks => 1);

my $nodename = 'rank';

for my $country ($doc->findnodes('/data/country')) {
  my ($node) = $country->findnodes($nodename);
  $country->removeChildNodes;
  $country->appendChild($node);
}

print $doc->toString(1);

__DATA__
<?xml version="1.0"?>
<data>
    <country name="Liechtenstein">
        <rank>1</rank>
        <year>2008</year>
        <gdppc>141100</gdppc>
        <neighbor name="Austria" direction="E"/>
        <neighbor name="Switzerland" direction="W"/> First Country
    </country>
    <country name="Singapore">
        <rank>4</rank>
        <year>2011</year>
        <gdppc>59900</gdppc>
        <neighbor name="Malaysia" direction="N"/> Second Country
    </country>
    <country name="Panama">
        <rank>68</rank>
        <year>2011</year>
        <gdppc>13600</gdppc>
        <neighbor name="Costa Rica" direction="W"/>
        <neighbor name="Colombia" direction="E"/> Third Country
    </country>
</data>

output

<?xml version="1.0"?>
<data>
  <country name="Liechtenstein">
    <rank>1</rank>
  </country>
  <country name="Singapore">
    <rank>4</rank>
  </country>
  <country name="Panama">
    <rank>68</rank>
  </country>
</data>
Borodin
  • 126,100
  • 9
  • 70
  • 144
2

with XML::Twig:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my $tag= shift @ARGV;
my $xml_file= shift @ARGV;

XML::Twig->new( twig_handlers => 
                  { 'country' => sub { foreach my $c ($_->children) 
                                         { $c->delete unless $c->is( $tag); } 
                                     },
                  },
                 pretty_print => 'indented',
              )->parsefile( $xml_file )
               ->print;
mirod
  • 15,923
  • 3
  • 45
  • 65
1

You can do it using Perl + XSLT. First you will need a XSLT document. The one below makes the transformation you require (you can test it here):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output indent="yes"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"></xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="year|gdppc|neighbor|country/text()" />
</xsl:stylesheet>

There are many libraries for XSLT (search CPAN or check XML::LibXSLT, which is the most popular one). See the first answer in this question for two alternatives. The second, using XML::LibXSLT::Easy is very simple and might be all you need:

use XML::LibXSLT::Easy;
my $p = XML::LibXSLT::Easy->new;
my $output = $p->process( xml => "data.xml", xsl => "stylesheet.xsl" );

The result of this transformation is:

<?xml version="1.0" encoding="UTF-8"?>
<data>
    <country name="Liechtenstein">
      <rank>1</rank>
   </country>
    <country name="Singapore">
      <rank>4</rank>
   </country>
    <country name="Panama">
      <rank>68</rank>
   </country>
</data>
Community
  • 1
  • 1
helderdarocha
  • 23,209
  • 4
  • 50
  • 65