StayCalm
StayCalm

Reputation: 145

Update an XML file using data read from a CSV file

I am trying to write a value to an XML file. I have a CSV input file Input.xsv and an XML template file template.xml which I need to update with the Name and IP Address values from the CSV file.

Input.csv (Name, IP Address)

LAB-1,10.26.0.1
LAB-2,10.26.0.2
LAB-3,10.26.0.3

template.xml

<?xml version="1.0" encoding="UTF-8"?>
<ns0:networkdevice
        xmlns:ns0="network.ers.ise.cisco.com" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:ns1="ers.ise.cisco.com"
        xmlns:ers="ers.ise.cisco.com"
        name="name1">
    <authenticationSettings>
        <enableKeyWrap>true</enableKeyWrap>
        <keyEncryptionKey>1234567890123456</keyEncryptionKey>
        <keyInputFormat>ASCII</keyInputFormat>
        <networkProtocol>RADIUS</networkProtocol>
        <radiusSharedSecret>aaa</radiusSharedSecret>
    </authenticationSettings>
    <coaPort>1700</coaPort>
    <NetworkDeviceIPList>
        <NetworkDeviceIP>
            <ipaddress>1.1.1.1</ipaddress>
            <mask>32</mask>
        </NetworkDeviceIP>
    </NetworkDeviceIPList>
</ns0:networkdevice>

I want to read names and IP addresses from the CSV file and to write them into my XML template file to create an HTTP POST request for each device in the CSV file.

I tried to save the values in my XML template, but I'm not sure if I'm doing it right.

#!/usr/bin/perl

use warnings;
use strict;

use Text::CSV;
use XML::TreeBuilder;
use Data::Dumper;

my $xmlbase    = 'template.xml';
my $paramsfile = 'input.csv';

# The list of tags filled from the CSV file
my @taglist = qw( name ipaddress );

my $tree = XML::TreeBuilder->new();
$tree->parsefile($xmlbase);

my $csv = Text::CSV->new();

open my $fh, "<:encoding(utf8)", $paramsfile or die "$paramsfile: $!";

while ( my $row = $csv->getline($fh) ) {

    my $contree = XML::TreeBuilder->new();
    $contree->parsefile($xmlbase);

    my $index = 0;

    for my $tagname ( @taglist ) {        
        my $tag = $contree->look_down('_tag' => $tagname);
        $tag->push_content($row->[$index++]);
    }
}

print '<?xml version="1.0" encoding="UTF-8"?>'."\n";
print $tree->as_XML();

After I run my script I get this error:

Can't locate object method "push_content"in this line

This error refers to the statement $tag->push_content($row->[$index++]);. I think it is because name does not have its own tag.

This element I have here:

<ns0:networkdevice
        xmlns:ns0="network.ers.ise.cisco.com"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:ns1="ers.ise.cisco.com"
        xmlns:ers="ers.ise.cisco.com"
        name="name1">

in tag ns0:networkdevice

When I try with just ipaddress it is printed, but with the original value from template.xml. It does not take a value from Input.csv, and if it was empty, it stays empty.

How should I do this?

Upvotes: 1

Views: 875

Answers (2)

salva
salva

Reputation: 10244

An easy way to generate XML from Perl is to use XML::FromPerl.

The template is replaced by a Perl data structure embedded in your code:

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use XML::FromPerl qw(xml_from_perl);

my $name = 'LAB-1';
my $ip   = '10.26.0.1';

my $xml = xml_from_perl
  [ 'ns0:networkdevice' => { 'xmlns:ns0' => 'network.ers.ise.cisco.com',
                 'xmlns:xs'  => 'http://www.w3.org/2001/XMLSchema',
                 'xmlns:ns1' => 'ers.ise.cisco.com',
                 'xmlns:ers' => 'ers.ise.cisco.com',
                 name => $name },
    [ authenticationSettings =>
      [ enableKeyWrap => 'true' ],
      [ keyEncryptionKey => '1234567890123456' ],
      [ keyInputFormat => 'ASCII' ],
      [ networkProtocol => 'RADIUS' ],
      [ radiusSharedSecret => 'aaa' ] ],
    [ coaPort => '1700' ],
    [ NetworkDeviceIPList =>
      [ NetworkDeviceIP =>
    [ ipaddress => $ip ],
    [ mask => 32 ] ] ] ];

say $xml->toString(1);

Upvotes: 1

Sobrique
Sobrique

Reputation: 53478

I'm not familiar enough with XML::Treebuilder so will give an example in XML::Twig. I will also avoid Text::CSV because I think it's overkill for most CSV use cases (e.g. any time you're not worrying about quoting/multi-line).

But it would go something like this:

#!/usr/bin/env perl

use strict;
use warnings;
use XML::Twig;

my $xml = XML::Twig -> new -> parsefile ( 'template.xml' );
$xml ->set_pretty_print('indented_a');

open ( my $input, '<', 'input.csv' ) or die $!;

while ( <$input> ) {
   chomp;
   my ( $name, $ip ) = split /,/;
   $xml -> root -> set_att('name', $name );
   $xml -> get_xpath('//ipaddress',0) -> set_text($ip);

   $xml -> print;
}

For the last line of your CSV, this will generate:

<?xml version="1.0" encoding="UTF-8"?>
<ns0:networkdevice
    name="LAB-3"
    xmlns:ers="ers.ise.cisco.com"
    xmlns:ns0="network.ers.ise.cisco.com"
    xmlns:ns1="ers.ise.cisco.com"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <authenticationSettings>
    <enableKeyWrap>true</enableKeyWrap>
    <keyEncryptionKey>1234567890123456</keyEncryptionKey>
    <keyInputFormat>ASCII</keyInputFormat>
    <networkProtocol>RADIUS</networkProtocol>
    <radiusSharedSecret>aaa</radiusSharedSecret>
  </authenticationSettings>
  <coaPort>1700</coaPort>
  <NetworkDeviceIPList>
    <NetworkDeviceIP>
      <ipaddress>10.26.0.3</ipaddress>
      <mask>32</mask>
    </NetworkDeviceIP>
  </NetworkDeviceIPList>
</ns0:networkdevice>

Which looks like what you're after?

You'd be wanting $xml -> sprint if you're wanting to include it in a POST or similar though.

Upvotes: 0

Related Questions