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
.