Keryn Drake
Keryn Drake

Reputation: 121

Add new nodes/elements to XML::LibXML object

Here is a basic XML document example

<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    </results>
</book_reviewers>

And I want to add this:

<reviewer>
    <name>Joan</name>
    <profession>Jett</profession>
 </reviewer>

I have tried a combinations of many solutions, here is one that, at least, doesn't throw an error, but, it also doesn't work.

#!/usr/bin/perl
use XML::LibXML;
use strict;

my $filename = "cr.xml";

my $parser = XML::LibXML->new();
my $critic_details = $parser->parse_file("$filename") or die;
my $new_reviewer = $critic_details->documentElement;
my $reviewer_name = $critic_details->documentElement;
my $reviewer_prof = $critic_details->documentElement;
my $newnode = $critic_details->documentElement;

 for my $reviewers($critic_details->findnodes("book_reviewers/results/reviewers")){
     $new_reviewer = $reviewers->createElement("reviewer");
    $reviewer_name = $new_reviewer->addChild("name");
    $reviewer_name->appendText("Joan");
    $reviewer_prof = $new_reviewer->addChild("profession");
    $reviewer_prof->appendText("Jett");
    $newnode = $reviewers->addSibling($new_reviewer); #also tried addChild

}
print $critic_details->toString;

The output that i get is:

<?xml version="1.0"?>
<book_reviewers>
        <results>
                <reviewer>
                        <name>Anne</name>
                        <profession>Catfish wrangler</profession>
                </reviewer>
                <reviewer>
                        <name>Bob</name>
                        <profession>Beer taster</profession>
                </reviewer>
                <reviewer>
                        <name>Charlie</name>
                        <profession>Gardener</profession>
                </reviewer>
        </results>
</book_reviewers>

Which is just the original data

Any help greatly appreciated - I am very new to both Perl and XML Cheers

Upvotes: 5

Views: 7763

Answers (3)

Miller
Miller

Reputation: 35208

It's possible to use XML::LibXML to essentially import the nodes from one XML Document into another. This is useful since you won't have to build the new data explicitly using objects, but instead can just use standard XML Notation.

The below demonstrates this. This could easily be expanded to important more than one new reviewer by setting a root node in the new document and then iterating over the found nodes instead of just selecting the first.

#!/usr/bin/perl

use strict;
use warnings;

use XML::LibXML;

my $dom = XML::LibXML->load_xml(IO => \*DATA);

my $newnode = XML::LibXML->load_xml(string => <<'END_STRING')->findnodes('//*')->[0];
<reviewer>
    <name>Joan</name>
    <profession>Jett</profession>
</reviewer>
END_STRING

$dom->findnodes("book_reviewers/results")->[0]->addChild($newnode);

print $dom->toString;

__DATA__
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    </results>
</book_reviewers>

Outputs:

<?xml version="1.0"?>
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    <reviewer>
    <name>Joan</name>
    <profession>Jett</profession>
</reviewer></results>
</book_reviewers>

Upvotes: 1

vanHoesel
vanHoesel

Reputation: 954

Just to help you out with some more neat things of XML::LibXML and get you to a bit more easy way to understand how XML works, I posted here another answer. Maybe you wil find some useful clues in it as well:

use strict;
use warnings;

use utf8;

use XML::LibXML;

my $filename = "cr.xml";

my $parser = XML::LibXML->new();
my $critic_details = $parser->parse_file("$filename") or die;

# find ALL the <book_reviewers><results> nodes
my @results = $critic_details->findnodes("book_reviewers/results");
die "no result node in xml-file" unless @results;

my ($name, $profession) = ("Joan", "Jett");

#
# Here gets the work done, put this in a loop for more entries
#

# add a new <reviewer> node to the LAST <results>
my $reviewer_node_child;
my $reviewer_node = $results[-1]->addNewChild(undef, "reviewer");

# create a child node
$reviewer_node_child =  $reviewer_node->addNewChild(undef, "name");
$reviewer_node_child->appendTextNode($name);

# create a child node
$reviewer_node_child =  $reviewer_node->addNewChild(undef, "profession");
$reviewer_node_child->appendTextNode($profession);

#
# Done the heavy power lifting
#

use XML::LibXML::PrettyPrint;
my $pretty = XML::LibXML::PrettyPrint->new(
  indent_string => ' ' x4,
  element       => {
    compact       => [qw| name profession | ],
    }
  );
$pretty->pretty_print($critic_details);

print $critic_details->toString;

__END__

The undef in the addNewChild methods are needed here. They are used to set the XML-NameSpace, which you will not need here, but in huge XML-documents is really the way to go.

Also, I added XML::LibXML::PrettyPrint just to show you how to get back to the nice pretty looking syntax. Forgive me for adding the use… add the end of the script.

which produces the this result:

<?xml version="1.0"?>
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
        <reviewer>
            <name>Joan</name>
            <profession>Jett</profession>
        </reviewer>
    </results>
</book_reviewers>

Enjoy XML and Perl, a very powerful but daunting combination of tools!

Upvotes: 4

KeepCalmAndCarryOn
KeepCalmAndCarryOn

Reputation: 9075

There are lots of ways to do this

This way creates the nodes separately and puts it together. It isn't production ready as it assumes there is a section

#!/usr/bin/perl
use XML::LibXML;
use strict;

my $filename = "cr.xml";

my $parser = XML::LibXML->new();
my $critic_details = $parser->parse_file("$filename") or die;

my $reviewer  = $critic_details->findnodes("book_reviewers/results")->[0];

my $node = XML::LibXML::Element->new("reviewer");

my $p = XML::LibXML::Element->new("profession");
my $pn = XML::LibXML::Text->new("Jett");
$p->addChild($pn);

my $n = XML::LibXML::Element->new("name");
my $nn = XML::LibXML::Text->new("Joan");
$n->addChild($nn);

$node->addChild($p);
$node->addChild($n);

$reviewer->addChild($node);

print $critic_details->toString;

which gives this

<?xml version="1.0"?>
<book_reviewers>
    <results>
        <reviewer>
            <name>Anne</name>
            <profession>Catfish wrangler</profession>
        </reviewer>
        <reviewer>
            <name>Bob</name>
            <profession>Beer taster</profession>
        </reviewer>
        <reviewer>
            <name>Charlie</name>
            <profession>Gardener</profession>
        </reviewer>
    <reviewer><profession>Jett</profession><name>Joan</name></reviewer></results>
</book_reviewers>

Upvotes: 5

Related Questions