1

I have this xml file test1.xml:

<body>
    <message>
        <name>gandalf</name>
        <attributes>
            <value key="1">1</value>
            <value key="2">2</value>
            <value key="3">3</value>
            <value key="4">4</value>
        </attributes>
    </message>
</body>

I want to override the value that its key is "4" to "10" so my xml will look like this:

<body>
    <message>
        <name>gandalf</name>
        <attributes>
            <value key="1">1</value>
            <value key="2">2</value>
            <value key="3">3</value>
            <value key="4">10</value>
        </attributes>
    </message>
</body>

this is my code:

#!/usr/bin/perl

use XML::Simple;

my $xml = new XML::Simple;
my $data = XMLin("test1.xml", ForceArray => 1);
$data->{message}->[0]->{attributes}->[0]->{value}->{4}->{content} = "10";
$newData = $xml->XMLout($data);
open(XML,">test2.xml");
print XML $newData;
close(XML);

when i run this code, the output xml looks like this:

<opt>
    <message>
        <name>gandalf</name>
        <attributes name="value">
            <1>1<1>
            <2>2<2>
            <3>3<3>
            <4>10<4>
        </attributes>
    </message>
</opt>
  • *Never* use `XML::Simple`, and *always* `use strict` and `use warnings 'all'` at the top of every Perl program. – Borodin May 22 '18 at 12:15
  • why never use XML::Simple – Gandalf The Gray May 22 '18 at 12:16
  • 2
    Its own documentation says *"PLEASE DO NOT USE THIS MODULE IN NEW CODE"* and *"The use of this module in new code is strongly discouraged. Other modules are available which provide more straightforward and consistent interfaces"* – Borodin May 22 '18 at 12:18
  • 2
    Take a look at [*Why is XML::Simple “Discouraged”?*](https://stackoverflow.com/questions/33267765/why-is-xmlsimple-discouraged) – Borodin May 22 '18 at 12:21
  • As the author of XML::Simple, I wholeheartedly agree with people who say don't use it. In fact I wrote [an XML::LibXML tutorial](http://grantm.github.io/perl-libxml-by-example/) to encourage people in that direction. The XML::Simple API is a mess that gives surprising results (like you got) and can't be fixed without breaking a lot of existing code. – Grant McLean May 23 '18 at 01:41

1 Answers1

5

Don't use XML::Simple.

XML::LibXML and XML::Twig are much better alternatives.

Here's the solution using XML::Twig:

#!/usr/bin/env perl
use strict;
use warnings;

use XML::Twig;

my $xml = XML::Twig -> new -> parsefile ( 'test1.xml' );
$_ -> set_text('10') for $xml -> get_xpath('//message/attributes/value[@key="4"]');
$xml -> set_pretty_print('indented');

$xml -> print;

This gives you:

<body>
  <message>
    <name>gandalf</name>
    <attributes>
      <value key="1">1</value>
      <value key="2">2</value>
      <value key="3">3</value>
      <value key="4">10</value>
    </attributes>
  </message>
</body>

You can print to a file, by opening a filehandle and giving that fh as an argument to print:

open ( my $ouput, '>', 'test2.xml' ) or die $!;
$xml -> print ( $output );

Because you also ask in comments:

I also want to know how to set text for a value with a key that doesnt exists. For example i want to add <value key="5">5</value> inside the attributes

my $attributes = $xml -> get_xpath('//message/attributes',0); #0 to find the first one. 
$attributes -> insert_new_elt('last_child', 'value', { key => 5 }, 5 );

Or as one line:

$xml -> get_xpath('//message/attributes',0) -> insert_new_elt('last_child', 'value', { key => 5 }, 5 );

Note the slightly different usage of get_xpath - we give a second argument 0 - because that says 'get the first element that matches', rather than every element that matches.

Sobrique
  • 52,974
  • 7
  • 60
  • 101
  • I also want to know how to set text for a value with a key that doesnt exists. For example i want to add 5 inside the attributes – Gandalf The Gray May 22 '18 at 13:28
  • Thank you so much, it worked and really helped! Regarding your edit, isnt there a built in function to update when exists and insert if doesnt? – Gandalf The Gray May 22 '18 at 14:07
  • No. `XML` doesn't work that way. You can have duplicate/repeated fields and nested fields in XML, so there isn't a 'create-if-it-doesn't-exist' function, because it's meaningless. But a test on a `get_xpath` call would work much the same way. – Sobrique May 22 '18 at 14:16