I'm not going to write the code for you, but I did have a similar requirement that a particular element could exist prior to me trying to insert it. So I wrote a quick "xml_child" routine that would look for a specific element, return it if it were there, but if it weren't, it would create it, then return that. You can also provide parms for setting some stuff if the element is being created, or other parms for setting stuff whether it's being created or updated.
=item xml_child
As a shortcut - given an XML element, find the child with the given na
+me, or, if there isn't one,
create it. This is the swiss-army-knife of XML functions for this pro
+ject.
If you do not understand this function after reading all the code, try
+ again or ask questions -
this function is called for 90% of the work done to this XML file.
Input: named parameters:
=over 4
=item parent
The parent object to look in.
=item child
The child name to look for/create
=item action
If the child needs to be created, this is the action to paste, see L<X
+ML::Twig>.
Default is 'last_child'.
=item relto
If the child needs to be created, this is where the action is relative
+ to.
=item def_text
If the child needs to be created, this is its default text, default to
+ blank.
=item def_attr
If the child needs to be created, this is its default attributes
=item override_attr
Whether or not the child existed prior, override its attributes with t
+hese
=item override_text
Whether or not the child existed prior, override its text with this.
=back
There is no option for sub elements as that would just get way too con
+fusing.
Output: XML::Twig::Elt
=cut
sub xml_child
{
my %opts = @_;
croak "parent option to xml_child must be XML::Twig::Elt"
unless $opts{parent} and ref $opts{parent} and $opts{parent}->
+isa('XML::Twig::Elt');
my $child_name = $opts{child};
#$child_name = "$child_name" unless $child_name =~ m.^/.;
my @children = $opts{parent}->children($child_name);
croak "$child_name matches too many descendants"
unless scalar @children < 2;
# If no such child, create it.
my $child;
if (@children)
{
$child = $children[0];
}
else
{
# here we need to create and paste a new element.
my $action = $opts{action} || 'last_child';
my $rel = $opts{relto} || $opts{parent};
$child_name =~ s#(?:.*/)?(\w+).*#$1#;
my @params = $child_name;
push @params, $opts{def_attr} if exists $opts{def_attr};
push @params, $opts{def_text} if exists $opts{def_text};
$child = XML::Twig::Elt->new(@params);
#eval {
$child->paste($action, $rel);
#}; if ($@)
#{
# print STDERR "pasting $action ", $rel->gi(), " (name = ",
+ $rel->att('name'), ") failed\n";
# die $@;
#}
}
$child->set_text($opts{override_text}) if exists $opts{override_te
+xt};
$child->set_att(%{$opts{override_attr}}) if exists $opts{override_
+attr};
$child;
}
Note that this was my first time using XML of any type, let alone XML::Twig, so be gentle with any criticisms ;-)
Example usage:
$child = xml_child(parent => $elt, child => 'built', action => 'after'
+, relto => $prev_child, def_text => '19700101 000000.000-0000');
$prev_child = $child; $child = xml_child(parent => $elt, child => 'sha
+reable', action => 'after', relto => $prev_child, override_text => 't
+rue');
Here I'm creating a series of elements, and even specifying order by the order I insert (although if it's already there out of order, then I won't correct the order). I also have some default text which won't change anything that is already present but I have some override text that says that I don't care what the XML owner says, I'm changing the shareable element to be "true".
This is probably adaptable to what you sound like you need.
If you're wondering why the lack of whitespace in the example - it's because I have about 30 of these... and I just needed to get it done - after it was done, it was more important to be able to pgup/pgdn through past these constants than to make it readable. ;-> If I were doing it again, I'd find a way to put this data somewhere else and loop through it.