0

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.

2
  • 1
    What have you tried? What problems are you having? Please show us your code. (Hint: the best solution almost certainly doesn't use XML::Simple.) Commented Aug 16, 2021 at 11:05
  • 1
    Entirely unrelated, but can save you some typing: the -> is optional between bracket subscripts, so you could use this instead: $xml->{outer_tag}[0]{string}[0]{default} = 'New_Name' Commented Aug 16, 2021 at 14:24

1 Answer 1

4

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I tried the XML::LibXML and got the below error: Can't call method "setData" on an undefined value at test_xml.pl line 7. am I missing something ?
@sbk It works when I run it with the input you provided. My guess is that your input file is not what you pasted in the question. I can't help you without knowing the actual structure of your data.
Looks like it's an environment issue. Thanks for solution. does it not replace the existing file ?
@sbk the snippets I provided do not replace the existing file, but you can open it and print to it. For instance, open my $FH, '>', $xml_file, then for XML::LibXML, a simple print $FH $dom->toString will work. For XML::Twig, you can do $xml->print($FH).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.