Home > Net >  How do I modify an XML element based on XML attribute content in Perl using XML::Simple?
How do I modify an XML element based on XML attribute content in Perl using XML::Simple?

Time:09-17

I have an XML of the below format:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<outer_tag>
        <inner_tag name="Name_0" template="Template_0">
        <string name="Name_1"><default>DEFAULT_NAME</default></string>
        <string name="Name_2"><default /></string>
        <string name="Name_3"><default></default></string>
        </inner_tag>
</outer_tag>

I need to modify the element with content "DEFAULT_NAME" , also, it's parent tag attribute needs to be "Name_1".

So, the output will be something like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<outer_tag>
        <inner_tag name="Name_0" template="Template_0">
        <string name="Name_1"><default>**NEW_NAME**</default></string>
        <string name="Name_2"><default /></string>
        <string name="Name_3"><default></default></string>
        </inner_tag>
</outer_tag>

I tried the below code:

#!/usr/bin/perl

use strict;
use warnings;

use XML::Simple;

my $xml_file = 'xml1.xml';

my $xml = XMLin(
    $xml_file,
    KeepRoot => 1,
    ForceArray => 1,
);

$xml->{outer_tag}->[0]->{string}->[0]->{default} = 'New_Name';

XMLout(
    $xml,
    KeepRoot => 1,
    NoAttr => 1,
    OutputFile => $xml_file,
);

But it's adding the New_Name1 at the end, not modifying the one I needed to replace. I'm new to XML in Perl.

CodePudding user response:

XML::Simple comes with the following warning:

PLEASE DO NOT USE THIS MODULE IN NEW CODE

As such, I would suggest to use another module (for instance, XML::Twig or XML::LibXML). If you have to use XML::Simple, see the end of my answer.

With XML::Twig, you could do:

use XML::Twig;

my $xml_file = 'xml1.xml';
my $twig = XML::Twig->new();
$twig->parsefile($xml_file);

my $node = ($twig->get_xpath('/outer_tag/inner_tag/string[@name="Name_1"]/default'))[0];
$node->set_text('New_Name');

$twig->print;

You can indent the output or print to a file if you want, see the documentation.

Or, using XML::LibXML:

use XML::LibXML;

my $xml_file = 'xml1.xml';
my $dom = XML::LibXML->load_xml(location => $xml_file);

my $node = ($dom->findnodes('/outer_tag/inner_tag/string[@name="Name_1"]/default/text()'))[0];
$node->setData('New_Name');

print $dom->toString

If you insist on using XML::Simple:

The reason your current code with XML::Simple is not working is that you assume what the structure of $xml is, and your assumption is wrong. With Data::Dumper or Data::Printer, you can easily check what your structure contains, and the problem becomes obvious:

use Data::Printer;

my $xml_file = 'xml1.xml';

my $xml = XMLin(
    $xml_file,
    KeepRoot => 1,
    ForceArray => 1,
    );

p $xml;

This will output:

\ {
    outer_tag   [
        [0] {
            inner_tag   {
                Name_0   {
                    string     {
                        Name_1   {
                            default   [
                                [0] "DEFAULT_NAME"
                            ]
                        },
....

It thus becomes obvious that you should have done

$xml->{outer_tag}->[0]->{inner_tag}->{Name_0}->{string}->{Name_1}->{default} = 'New_Name';

instead of

$xml->{outer_tag}->[0]->{string}->[0]->{default} = 'New_Name';

That being said, use XML::Twig or XML::LibXML.

  • Related